为什么我的整数数学与std :: pow给出了错误的答案?

use*_*257 21 c++ floating-point g++

考虑以下代码:

#include <iostream>
#include <cmath>

int main() {
    int i = 23;
    int j = 1;
    int base = 10;
    int k = 2;
    i += j * pow(base, k);
    std::cout << i << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

它输出"122"而不是"123".这是g ++ 4.7.2(MinGW,Windows XP)中的错误吗?

And*_*owl 10

std::pow()使用浮点数,它没有无限的精度,可能你正在使用的标准库的实现pow()以(差)方式使得缺乏无限精度变得相关.

但是,您可以轻松定义适用于整数的自己的版本.在C++ 11中,您甚至可以创建它constexpr(以便在可能的情况下在编译时计算结果):

constexpr int int_pow(int b, int e)
{
    return (e == 0) ? 1 : b * int_pow(b, e - 1);
}
Run Code Online (Sandbox Code Playgroud)

这是一个实例.


尾递归形式(Dan Nissenbaum学分):

constexpr int int_pow(int b, int e, int res = 1)
{
    return (e == 0) ? res : int_pow(b, e - 1, b * res);
}
Run Code Online (Sandbox Code Playgroud)

  • 这个答案无法确定核心问题.它声明"std :: pow()使用浮点数,它没有无限精度",但缺乏精度并不是"pow"返回错误结果的原因.参数和"pow"的返回类型有足够的精度来返回精确的结果.实际问题是质量差的"战俘"实施.一个好的`pow`实现将为pow(10.,2.)`返回1024.如果`45%7`返回2,那么归咎于浮点运算就像归怪整数运算一样. (6认同)
  • 但通常情况下,当输入和输出都是(小)整数时,你会期望浮点运算能够正常工作.(1.0 + 1.0 == 2.0,没有舍入错误,例如).所以我不能责怪OP因为**特殊情况(即pow(10.0,2.0)!= 100.0`中引入的舍入错误而感到困惑) (5认同)
  • @DanNissenbaum:你为什么还在冒这个混乱?我之前告诉过你,你拒绝听; 实施并不关心你的意图.这不是在读书业务.它看到了123.0,并且知道将123.0转到122是疯了.就像我之前说的那样,浮点数不一定是近似值,它们不代表实数的范围.每个代表一个实数.现代FPU不会出错.请,请了解浮点的工作原理. (5认同)
  • @DanNissenbaum:根据IEEE浮点运算标准(754-2008),第3.3节,浮点格式表示(-1)**s•b**e•m,其中s为0或1,e为区间中的整数取决于格式,m是由某个数字字符串表示的数字,具体取决于格式(相当于某个范围内的整数除以基数的某个幂),+ +无穷大,-infinity,安静的NaN和信号NaN.**标准中没有**表示浮点对象表示间隔.这是谣言传递的不正常的常见传说,而不是规范. (4认同)
  • @DanNissenbaum:不,该死的.`pow`就是问题所在.实现将`100.0`转换为100以外的任何东西都是非法的.显然是99,这意味着**`pow`没有返回'100.0`.** (4认同)
  • @DanNissenbaum:建议将整数"100.0"舍入为"边缘情况"是疯狂的.在正确结果的1 ulp内没有"pow"实现可能产生除"100.0"以外的任何值.去了解浮点数如何与其他人一起工作; 当你显然不想吸收它时,我厌倦了试图强迫你的信息. (4认同)
  • -1只是为了提出天真的`int_pow`实现.学习基础知识[这里](https://en.wikipedia.org/wiki/Exponentiation_by_squaring). (3认同)
  • @DanNissenbaum:这是不正确的.浮点数能够表示由尾数中的位数确定的范围内的精确整数,并且正确舍入的"pow()"将返回该范围内整数的精确结果. (3认同)
  • @DanNissenbaum:请读一下Eric Postpischil所说的话.100可以用任何标准浮点类型精确表示.一个体面的"战俘"至少应该让确切的情况正确,其中`pow(10.,2.)`就是一个.因此,为`pow(10.,2.)`返回100以外的东西的`pow`是一个糟糕的`pow`.`pow`返回错误的结果,因此舍入给出了错误的整数.这不是四舍五入被打破. (3认同)
  • @DanNissenbaum:我对你使用"与100的精确值一致"这句话感到困扰.浮点数不是模糊的东西,计算机可以做任何想做的事情而且你搞砸了.它们是符号位,有效位数和指数.加法和减法以及朋友们都有明确定义的语义.转换为整数也是如此.你应该熟悉它们的工作方式.你谈到"偏见"和"运行时系统无法知道浮点值[...]是一个精确的整数." 关于浮点如何工作的混乱想法. (3认同)
  • @DanNissenbaum:具体来说,`100.0`*是*一个完全整数.它不是"与"整数或任何类型的"一致"; 它*是一个精确的整数,恰好表示为有效数乘以2 <sup> exponent </ sup>.当被问及什么是'pow(10.,2.)`时,一个好的'pow`该死的应该返回那个值,'100.0`.如果确实如此,那么明确定义的转换为整数就可以得到明确定义的东西,你就得到了整数"100". (3认同)
  • @DanNissenbaum:不,浮点数只代表一个值.他们没有用户愿望的概念.围绕该浮点数的区间内的数字可以四舍五入到该浮点数,但是123.0表示123.0而从不表示123-2 <sup> -1000 </ sup>或任何类型.就像我说的那样,你应该好好学习这些东西是如何运作的. (3认同)
  • @DanNissenbaum:你为什么怀疑OP的系统`pow`被打破了?显然是.为什么你假设复杂的东西(`pow`)是完全无瑕的(当它是一个开放的研究问题,找到一个完全无瑕的`pow`)而简单的事情(double-int转换)表现在这些完全疯狂的方式?去了解浮点数.互联网上充满了有用的资源. (2认同)

Eri*_*hil 7

到目前为止,所有其他答案都错过或围绕问题中唯一的一个问题跳舞:

pow你的C ++实现的质量不高。当没有必要时,它会返回一个不准确的答案。

获得更好的 C++ 实现,或者至少替换其中的数学函数。Pascal Cuoq 指出的那个很好。

  • @sellibitze:我没有写任何关于依赖实施质量的文章。工程包括,除其他外,从规范中得出结果。如果您有一个可信规范,即您的库在可表示时返回精确结果,那么您可以依赖它。如果您没有这样的规范,那么您可能不会依赖它。 (2认同)

Chr*_*our 3

至少我的没有:

$ g++ --version | head -1
g++ (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2)

$ ./a.out 
123
Run Code Online (Sandbox Code Playgroud)

IDEone也运行版本 4.7.2 并提供123.


签名pow()来自http://www.cplusplus.com/reference/cmath/pow/

     double pow (      double base,      double exponent );
long double pow ( long double base, long double exponent );
      float pow (       float base,       float exponent );
     double pow (      double base,         int exponent );
long double pow ( long double base,         int exponent );
Run Code Online (Sandbox Code Playgroud)

您应该设置double base = 10.0;double i = 23.0

  • 您所展示的只是至少存在一个 GCC 版本,其行为符合 OP 的预期。这并没有回答OP的版本是否有错误,或者该行为在标准下是允许的问题 (6认同)