【问题标题】:I got different results with pow(10,2) and pow(10,j), j=2;我用 pow(10,2) 和 pow(10,j), j=2; 得到了不同的结果
【发布时间】:2013-10-01 22:06:30
【问题描述】:

这个打印100:

int j=2;
int i= pow(10,2);  
printf("%d\n", i);

这个打印 99:

int j=2;
int i= pow(10,j);  
printf("%d\n", i);

为什么?

【问题讨论】:

  • 你使用的是什么编译器,在什么系统上?
  • 我猜这是双精度结果的整数截断,看看"%f" 的输出是什么样子会很有趣。
  • @MSalters:不,这绝对是 MS 的错,或者更确切地说是 mingw 使用有缺陷的 MS 代码的错。 Mingw 是使用 GCC 作为编译器和 MSVCRT.DLL(微软的 libc)作为标准库的 C 实现。越野车pow 是后者的一部分。
  • @MSalters:如前所述,它可能符合要求,但即便如此,它的实施质量也极低。没有理由让函数为参数返回错误的结果,其中正确的结果完全可以表示并且可以正确计算。编译器的优化选择解释了差异,但如果库版本返回正确的结果,就不会有任何差异。
  • @MSalters:正如严格不等式所述,该属性不可能通过简单的计数参数来满足;唯一可能的方法是 pow(x,y)==x 对于所有 x,y。如果您将其修复为使用<= 而不是<,那么任何正确处理精确结果的理智实现都将满足您的(修改后的)条件。

标签: c floating-point pow


【解决方案1】:

发生的情况是,您有一个 C 实现,其标准库的 pow 实现质量非常低,即使确切的结果可以在类型 (double) 中表示,它也会返回不精确的结果。对pow(10,2) 的调用似乎产生了刚好低于100.0 的值,当四舍五入为整数时,产生99。当参数为常量时你看不到这一点的原因是编译器冒昧地进行了优化一起调用并在编译时将其替换为常量 100。

如果您的意图是进行整数幂运算,请不要使用 pow 函数。写一个合适的整数幂函数,或者当指数已知时,直接写出乘法。

【讨论】:

  • 是的,即使 pow 库确实计算了正确四舍五入的精确结果,依赖每个整数都以 double 精确表示是脆弱的。我正在等待 64 位 int 的出现问为什么 pow(11,18) 没有正确计算。
  • 从这个角度来看,实际上它是安全的。 int 在实际(非 DSP)实现中永远不会改变,因为与整数提升的意外影响有关的许多问题、从标准类型中省略一个大小步长的问题以及简单的遗留兼容性问题。
【解决方案2】:

在第一种情况下,我怀疑编译器已将值优化为 10*10 而没有实际调用 pow(编译器确实这样做了)。在第二种情况下,看起来您有浮点舍入错误。结果是几乎 100,但不完全是,隐式转换为int 会截断它。

pow 函数作用于double,而不是int

通常(但不总是),当您将双精度数转换为整数时,您调用round()。例如:

int i = (int) round(pow(10,j));

如果你的 C 库没有这个,你可以模仿:

#define round(x) floor((x)+0.5)

【讨论】:

  • 小错字:pow(10, 2) == 10*10 不是10 << 1
【解决方案3】:

pow 返回double,因此如果结果不完全是100,但稍小于转换为int 时将截断,您将收到您所看到的99 结果。看看double 变量和%f 格式的结果是什么样子会很有趣。

使用文字时看不到它的原因是因为constant folding 这将导致对pow 的调用被优化并替换为常量。当我们生成程序集时,我们可以看到使用常量的版本没有调用pow,它只是移动了最终结果100看到它live):

movl    $100, -4(%rbp)

而第二个版本实际上调用了pow(看到它live):

call    pow

【讨论】:

  • 我两次都得到了 100 双倍。
猜你喜欢
  • 1970-01-01
  • 2014-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-17
  • 2019-04-04
  • 2020-03-21
  • 1970-01-01
相关资源
最近更新 更多