【问题标题】:C: double to int conversion error in gcc 6.3.0C:gcc 6.3.0 中的 double 到 int 转换错误
【发布时间】:2019-06-27 15:14:05
【问题描述】:

我有以下 c 程序,有时我运行它,输出会有所不同,具体取决于编译器和平台。我了解 double 到 int 的转换可能会导致问题。

代码如下:

//Compiler version gcc 6.3.0
#include <stdio.h>
#include <math.h>

int main(void){
    double d = 2;
    printf("%.20lf\n", pow(10, d));
    printf("%d\n", (int)pow(10, d));
    printf("%d\n", (int)pow(10, 2));
}

100是期望值,但语句

    printf("%d\n", (int)pow(10, d));

当我同时使用 gcc 6.3.0 和 Windows 10 x64 时,输出为 99,但在其他情况下没有。

以下是一些结果:

//gcc 6.3.0 (Sublime Text 3) in Windows 10 x64
100.00000000000000000000
99 ->this is the problem
100

//gcc 6.3.0 in Android (using Dcoder app)
100.00000000000000000000
100
100

//MSVC(VS 2017 x86) in Windows 10 x64
100.00000000000000000000
100
100

我还测试了一些在线 gcc(6.3.0) 编译器,但所有输出都是 100。

感谢您的帮助。

【问题讨论】:

  • 使用(int)round(pow(10, d)) 应该修复第二个。第三个pow(10, 2) 可能被优化编译器硬编码为100
  • @user3386109:这不是that question 的重复,因为浮点格式完全能够准确地表示 100,因此错误不是由于浮点运算的特性造成的。这是由于 pow 的实现不足造成的。
  • 发布的代码确实存在一些语法问题。第一个是double d = 2; 试图从int 初始化double。声明应为:double d = 2.0;

标签: c gcc output


【解决方案1】:

pow 的某些实现返回值不同于 pow(10, 2) 的 100。例如,可能会返回 99.9999999999999857891452847979962825775146484375。当这个double 值转换为int 时,结果是99。

这是pow 实现的质量问题。好的pow 实现在可能的精确结果时返回精确结果。

此外,我怀疑printf("%.20lf\n", pow(10, d)) 在 Windows 实现中的格式不正确——它可能在将其转换为 15 位有效十进制数字之前将其转换为 20 位数字以供输出。您可以通过打印printf("%.20g\n", pow(10, d)-100) 来测试它。这将在 double 算术中从 pow(10, d) 中减去 100。如果它显示非零输出,则您知道 pow(10, d) 不完全是 100,因此 printf("%.20lf\n", pow(10, d)) 显示不正确的结果。

请注意,pow 是一个很难实现的函数。只有有限数量的案例有准确的结果,所以大多数结果必然是不准确的。然而,即使在这些情况下,要获得一个正确舍入的结果(四舍五入到最接近的可表示值)也很困难。据我所知,没有人以这种质量实现pow。大多数pow 实现允许一些额外的错误,因此您不应该依赖pow 来正确舍入。但是,可以实现pow,以便可以得到精确结果的情况确实返回精确结果,并且一些实现实现了这一点。

【讨论】:

  • gcc 6.3.0 是否使用微软软件计算pow()?我会假设它使用 glibc 代替。顺便说一句,我不知道有任何pow 实现总是在可能的精确结果时提供精确结果。
  • @njuffa:我相信 macOS pow 在可表示精确结果时会提供精确结果。我知道 GCC 是“只是”编译器,需要提供一个标准库,但我不熟悉 Windows 的常见打包和典型安装配置。
  • 我期待发布有关 macOS pow 的声明的证据。
  • @njuffa:我认为您可以手动列出(并因此测试)确切的案例:pow(x, 0)pow(x, 1)pow(x, -1) for x 是 2 的幂,pow(x, n)对于整数n>1,其中x 没有太多位,以至于x^n 变得太宽,平方根、第四根、第八根等的数量有限。
  • @njuffa:作者向我确认了 macOS pow 在可表示时返回准确的结果。这是因为它具有亚 ULP 精度。每个结果与理想结果相差不到 1 ULP。当结果可表示时,该值是与理想结果相差小于 1 ULP 的唯一可表示值。
猜你喜欢
  • 1970-01-01
  • 2015-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-05
  • 2013-05-31
  • 2012-05-10
相关资源
最近更新 更多