我感觉我一定是找不到它了。C++ 有什么理由吗? pow 函数不实现“power”函数,除了 floatdouble是?

我知道实现很简单,我只是觉得我正在做应该在标准库中的工作。稳健的幂函数(即以某种一致、明确的方式处理溢出)写起来并不有趣。

有帮助吗?

解决方案

作为事实上,它的作用。

由于C ++ 11有pow(int, int)的模板实现---甚至更一般的情况,见(7)在 http://en.cppreference.com/w/cpp/numeric/math/ POW


编辑:纯粹主义者可能会说这是不正确的,因为实际上存在“提升”使用打字。这种或那种方式,可以得到正确的结果int或错误,上int参数。

其他提示

作为 C++11, ,特殊情况被添加到幂函数(和其他)套件中。 C++11 [c.math] /11 状态,在列出所有 float/double/long double 重载(我的重点,并解释):

此外,应有足够的额外过载以确保, 如果任何参数对应于 double 参数有类型 double 或整数类型,则所有参数对应 double 参数被有效地转换为 double.

因此,基本上,整数参数将升级为双精度来执行操作。


之前 C++11 (当你提出问题时),不存在整数重载。

由于我与创作者的关系并不密切 C 也不 C++ 在他们被创造的日子里(尽管我 相当旧),也不属于创建标准的 ANSI/ISO 委员会,这必然是我的观点。我想这是 知情的 意见,但是,正如我妻子会告诉你的那样(经常并且不需要太多鼓励),我以前就错了:-)

就其价值而言,假设如下。

怀疑 原来的 ANSI 之前的原因 C 没有这个功能是因为它完全没有必要。首先,已经有一种非常好的方法来执行整数幂(使用双精度数,然后简单地转换回整数,在转换之前检查整数溢出和下溢)。

其次,你必须记住的另一件事是,其初衷 C 是作为 系统 编程语言,浮点在这个领域是否值得怀疑是值得怀疑的。

由于其最初的用例之一是编写 UNIX 代码,因此浮点几乎毫无用处。C 所基于的 BCPL 也没有使用幂(根据记忆,它根本没有浮点数)。

顺便说一句,积分幂运算符可能是二元运算符而不是库调用。您不将两个整数相加 x = add (y, z) 但与 x = y + z - 的一部分 语言本身 而不是图书馆。

第三,由于积分算力的实现相对微不足道,因此几乎可以肯定,该语言的开发人员会更好地利用他们的时间提供更有用的东西(请参阅下面关于机会成本的评论)。

这也与原版相关 C++. 。由于最初的实现实际上只是一个翻译器,它产生了 C 代码,它继承了许多属性 C. 。它的初衷是带有类的 C,而不是带有类的 C 加上一点额外的数学东西。

至于为什么之前从未将其添加到标准中 C++11, ,您必须记住,标准制定机构有具体的指导方针需要遵循。例如,ANSI C 专门负责编纂现有实践, 不是 创建一种新的语言。否则,他们可能会发疯并给我们艾达:-)

该标准的后续版本也有具体的指导方针,可以在基本原理文件中找到(关于委员会为何做出某些决定的基本原理,而不是语言本身的基本原理)。

例如 C99 理由文件具体继承了其中两项 C89 限制可添加内容的指导原则:

  • 保持语言小而简单。
  • 仅提供一种执行操作的方法。

指南(不一定是那些 具体的 )是为各个工作组制定的,因此限制了 C++ 委员会(以及所有其他 ISO 团体)。

此外,标准制定机构认识到,存在一个 机会成本 (一个经济术语,意思是你必须放弃什么才能做出决定)他们做出的每一个决定。例如,购买价值 10,000 美元的超级游戏机的机会成本是亲切的关系(或者可能是 全部 与你的另一半的关系)大约六个月。

埃里克·冈纳森(Eric Gunnerson)用他的观点很好地解释了这一点 -100分说明 至于为什么微软的产品并不总是添加一些东西——基本上,一个功能一开始就有100分的漏洞,所以它必须增加相当多的价值才能被考虑。

换句话说,您愿意在标准中添加一个完整的电源运算符(老实说,任何半点像样的编码器都可以在十分钟内完成)还是多线程?对于我自己来说,我更喜欢后者,而不必纠结于 UNIX 和 Windows 下的不同实现。

我还希望看到标准库中成千上万的集合(哈希、btree、红黑树、字典、任意映射等),但是,正如其基本原理所述:

标准是实施者和程序员之间的条约。

标准机构中实施者的数量远远超过程序员的数量(或者至少是那些不了解机会成本的程序员)。如果添加所有这些东西,下一个标准 C++ 将会 C++215x 三百年后,编译器开发人员可能会完全实现它。

无论如何,这就是我对此事的(相当长的)想法。如果只根据数量而不是质量来进行投票,我很快就会把其他人都打得落花流水。感谢收听 :-)

无论如何,对于任何固定宽度整数类型,几乎所有可能的输入对都会溢出该类型。如果一个函数对于绝大多数可能的输入都没有给出有用的结果,那么标准化这个函数有什么用呢?

您几乎需要有一个大整数类型才能使该函数有用,并且大多数大整数库都提供该函数。


编辑: 在对该问题的评论中,static_rtti 写道“大多数输入导致它溢出?对于 exp 和 double pow 也是如此,我没有看到有人抱怨。”这是不正确的。

我们先把它放在一边 exp, ,因为这不是重点(尽管这实际上会让我的观点更有说服力),并重点关注 double pow(double x, double y). 。对于 (x,y) 对的哪一部分,该函数做了一些有用的事情(即,不仅仅是上溢或下溢)?

实际上我只会关注一小部分输入对 pow 有道理,因为这足以证明我的观点:如果x是正的,| y | <= 1,然后 pow 不会溢出或下溢。这包含了所有浮点对的近四分之一(正好有一半的非 NaN 浮点数是正数,只有不到一半的非 NaN 浮点数的大小小于 1)。显然,有 很多 其他输入对的 pow 产生有用的结果,但我们已经确定它至少占所有输入的四分之一。

现在让我们看一下固定宽度(即非大数)整数幂函数。对于哪部分输入,它不会简单地溢出?为了最大化有意义的输入对的数量,应该对基数进行签名,而对指数进行无符号签名。假设底数和指数都是 n 位宽。我们可以很容易地得到有意义的输入部分的界限:

  • 如果指数为0或1,那么任何底数都有意义。
  • 如果指数为 2 或更大,则大于 2^(n/2) 的底数不会产生有意义的结果。

因此,在 2^(2n) 个输入对中,少于 2^(n+1) + 2^(3n/2) 个输入对会产生有意义的结果。如果我们看看最常见的用法,即 32 位整数,这意味着输入对的百分之一的 1/1000 的量级不会简单地溢出。

由于没有办法来表示一个int所有整数幂反正:

>>> print 2**-4
0.0625

这实际上是一个有趣的问题。我在讨论中没有找到的一个论点是参数缺乏明显的返回值。让我们来数一下假设的方式 int pow_int(int, int) 功能可能会失败。

  1. 溢出
  2. 结果未定义 pow_int(0,0)
  3. 结果无法表示 pow_int(2,-1)

该功能至少有 2 种故障模式。整数不能表示这些值,在这些情况下函数的行为需要由标准定义 - 并且程序员需要了解函数如何准确地处理这些情况。

总体而言,忽略该功能似乎是唯一明智的选择。程序员可以使用带有所有可用错误报告的浮点版本。

短的答案:

pow(x, n)的到n是自然数甲专业化往往是有用的时间性能。但是,标准库的通用pow()依然工作得(的出奇!的)以及用于这一目的,它绝对是至关重要的,包括在标准C库中尽量少,因此它可以被制成便携式和容易实现成为可能。在另一方面,完全不从C ++标准库或STL,我敢肯定没有人会使用某种嵌入式平台的计划被停止。

现在,对于长的答案。

pow(x, n)可以通过专门n的自然数被快得多制成的情况较多。我不得不用我自己的实现这个功能的几乎每一个项目我写的(但我用C编写了大量的数学课程)。该专业操作可以在O(log(n))时间来完成,但是当n小,更简单的线性版本可以更快。这里有两种实施方式:


    // Computes x^n, where n is a natural number.
    double pown(double x, unsigned n)
    {
        double y = 1;
        // n = 2*d + r. x^n = (x^2)^d * x^r.
        unsigned d = n >> 1;
        unsigned r = n & 1;
        double x_2_d = d == 0? 1 : pown(x*x, d);
        double x_r = r == 0? 1 : x;
        return x_2_d*x_r;
    }
    // The linear implementation.
    double pown_l(double x, unsigned n)
    {
        double y = 1;
        for (unsigned i = 0; i < n; i++)
            y *= x;
        return y;
    }

(I左x和返回值作为双打因为pow(double x, unsigned n)的结果将适合在双重大约尽可能经常pow(double, double)会。)

(是,pown是递归的,但断裂堆栈是绝对不可能的,因为最大堆栈大小将大致相等log_2(n)n是整数。如果n是一个64位的整数,即给你的关于最大堆栈大小64. 没有硬件具有这样的极端存储器的限制,不同的是用硬件堆栈一些狡猾的PIC,只有去3至8个函数调用深。)

至于性能,你会通过什么花园各种pow(double, double)能够感到惊讶。我在我5岁的IBM Thinkpad测试一亿次迭代x等于迭代次数和n等于10在这种情况下,pown_l韩元。的glibc pow()了12.0用户秒,pown了7.4用户秒,pown_l只用了6.5用户秒。所以,这不是太奇怪了。我们或多或少期待此。

然后,我让x是恒定的(I将其设置为2.5),并且我环n从0到19亿次。这一次,非常意外,glibc的pow赢了,以压倒性优势!只用了2.0秒的用户。我pown了9.6秒和pown_l了12.2秒。这里发生了什么?我做了另外一个测试来了解一下。

我做同样的事情如上述仅x等于100万。这一次,pown赢得了9.6s。 pown_l了12.2S和glibc POW了16.3s。现在,很明显! glibc的pow执行比三个时x低较好,但是当x高最差。当x为高时,执行pown_l最好当n低,pown执行最好当x高。

因此,这里有三种不同的算法,每一个都能够比在正常情况下的其他表现更好。因此,最终,这最有可能使用取决于你如何要使用pow,但使用的是正确版本的的值得的,并且具有所有版本的规划是很好的。事实上,你甚至可以用这样的功能自动算法的选择:

double pown_auto(double x, unsigned n, double x_expected, unsigned n_expected) {
    if (x_expected < x_threshold)
        return pow(x, n);
    if (n_expected < n_threshold)
        return pown_l(x, n);
    return pown(x, n);
}

只要x_expectedn_expected为常数决定在编译时,可能有一些其他的警告沿,一个优化编译器值得其盐将自动删除整个pown_auto函数调用和与三个的适当的选择取代它算法。 (现在,如果你实际去尝试的使用的这一点,你可能必须与它的一个小玩具,因为我并没有完全尝试的编译的我“d上面写;))

在另一方面,glibc的pow不工作的和glibc是足够大了。 C标准应该是便携式的,包括各种的嵌入式设备的(其实嵌入式开发人员到处普遍认同的glibc已经是他们太大),它不能是便携式的,如果因为一次简单的数学函数,它需要包括每一个替代算法的可能的是有用的。所以,这就是为什么它是不是在C标准。

注脚:在时间的性能测试,我给我的功能相对宽松的优化参数(-s -O2),它们可能比相媲美,如果不是糟糕的是,什么是可能用来编译glibc的我的系统(ArchLinux的),所以其结果可能是公正的。对于更严格的测试,我不得不编译glibc的我和我的 reeeally 的不喜欢这样做。我用的Gentoo,所以我记得它有多长,即使任务的自动的。结果是我确凿(或相当不确定的)就够了。你当然欢迎这种自己做。

加成轮:pow(x, n)的专业化的所有整数是仪器如果需要一个准确的整数输出,这确实会发生。考虑具有p ^ N元素的N维阵列分配存储器。获取P 1 n关闭甚至由一个将导致可能随机发生段错误。

有C ++不具有附加重载的一个原因是是兼容C

C ++ 98具有像double pow(double, int)功能,但这些已在C ++ 11用参数移除C99不包括它们。

的http:// WWW。 open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3286.html#550

获得稍微更精确的结果还意味着获得略微不同结果。

世界在不断发展,编程语言也在不断发展。这 C 小数点 TR 的第四部分1 增加了一些更多的功能 <math.h>. 。这个问题可能对这些函数的两个系列感兴趣:

  • pown 函数,它需要一个浮点数和一个 intmax_t 指数。
  • powr 函数,需要两个浮点数(xy)并计算 x 权力 y 用公式 exp(y*log(x)).

看来标准制定者最终认为这些功能足够有用,可以集成到标准库中。然而,合理的理由是这些功能是由 ISO/IEC/IEEE 60559:2011 二进制和十进制浮点数的标准。我不能肯定地说 C89 时遵循的“标准”是什么,但未来的演变 <math.h> 可能会受到未来发展的重大影响 ISO/IEC/IEEE 60559 标准。

请注意,小数 TR 的第四部分不会包含在 C2x(下一个主要 C 修订版)中,并且可能稍后作为可选功能包含在内。据我所知,没有任何意图将 TR 的这一部分包含在未来的 C++ 修订版中。


¹ 您可以找到一些正在进行的文档 这里.

或许是因为该处理器的ALU没有实现用于整数这样的功能,但存在这样的FPU的指令(如斯蒂芬所指出的,它实际上是一对)。因此,它实际上是更快地投翻一番,呼叫战俘与双打,然后测试溢出,并投退,比使用整数运算来实现它。

(一两件事,对数减少权力乘法,但整数对数失去大多数输入了很多的精度)

斯蒂芬是正确的,在现代处理器这不再是真实的,但是,当选择了(刚才使用的C函数C ++)数学函数的C标准是什么现在,20岁的?

一个很简单的原因是:

5^-2 = 1/25

在STL库一切是基于最精确,鲁棒的东西可想而知。肯定的是,INT将返回到零(来自1/25),但是,这将是不准确的答案。

我同意,这是在某些情况下,不可思议。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top