繁体   English   中英

不同编译器的双精度不同行为

[英]different behaviours for double precision on different compiler

我的代码很简单

double d = 405, g = 9.8, v = 63;
double r = d * g / (v * v);
printf("%s\n",(r>1.0)?"GT":"LE");

这是我的结果

  • g ++ - mingw32-v4.8.1:LE (结果确实是EQ)
  • 关于ubuntu的g ++:GT(这个结果来自我的朋友,只是手头没有linux)
  • VC ++ 11:GT
  • C#(。NET 4.5):GT
  • Python v2.7.3:GT(这也来自我的朋友)
  • Haskell(GHCi v7.6.3):GT

g ++ - mingw,vc ++,c#,haskell都在我的机器上运行,带有i7-2630QM

Python-win32版本来自我的朋友,他也从他的g ++获得LE - mingw-3.4.2。

ubuntu版本来自另一位朋友......

只有g ++给我LE,其他都是GT。

我只是想知道哪一个是错的,g ++或其他。

或者应该是什么,GT或LE,IEEE 754?

IEEE 754 64位二进制结果是GT。

包含9.8的两个完全可表示的值是:

9.7999999999999989341858963598497211933135986328125
9.800000000000000710542735760100185871124267578125

第二个更接近9.8,因此应该在正常的舍入模式中选择它。 它略大于9.8,导致产品略大于实数算术产生的产品,3969.00000000000045474735088646411895751953125。 v转换为double是精确的, v*v乘法也是如此。 结果是将数字略大于3969除以3969.舍入结果为1.0000000000000002220446049250313080847263336181640625

当评估d * g时可能会出现差异,因为该产品的数学结果必须向上舍入以产生可表示为double的值,但是long double结果更准确。

大多数编译器将40563转换9.8精确double并将9.8转换为9.800000000000000710542735760100185871124267578125,尽管C ++标准为它们提供了一些余地。 v * v的评估通常也是精确的,因为数学结果是完全可表示的。

通常,在英特尔处理器上,编译器可以通过以下两种方式之一来评估d * g :使用double算法或使用带有英特尔80位浮点格式的long double精度。 当使用double计算时,405•9.800000000000000710542735760100185871124267578125生成3969.00000000000045474735088646411895751953125,并将其除以3969会产生略大于1的数字。

使用long double评估时,产品为3969.000000000000287769807982840575277805328369140625。 该产品虽然大于3969,但略小,使用long double算法除以3969会产生1.000000000000000072533125339280246635098592378199100494384765625。 将此值分配给r ,编译器需要将其转换为double (额外的精度也可以仅在中间表达式中使用,而不是在分配或管型。)此值是足够接近一个舍入它double产生一个。

您可以通过对每个单独的操作使用强制转换或赋值来减轻编译器之间的某些(但不是全部)变化:

double t0 = d * g;
double t1 = v * v;
double r = t0/t1;

回答你的问题:因为表达式应该评估为“相等”,所以测试r>1.0应该是假的,并且打印的结果应该是"LE"

实际上,你遇到的问题是像9.8这样的数字不能完全表示为浮点数(网上有数百个好的链接来解释为什么会这样)。 如果您需要精确的数学运算,则必须使用整数。 或者bigDecimal 或者一些这样的事情。

只有当小数部分可以通过二维分数(如0.5,0.25,......等)求和时,从小数部分到二进制分数的转换才是精确的。

示例中的数字9.8包含分数0.8,它不能使用二进制数系统表示为精确分数。 因此,根据表示小数的精度,不同的编译器会给出不同的结果。

使用数字9.75运行程序,然后所有编译器都会给你相同的结果,因为

0.75 = 0.25 + 0.125 = 2 -2 + 2 -3

因此,可以使用二进制分数精确表示数字9.75。

我测试了代码,这应该返回GT。

int main() {
    double d = 405.0f, g = 9.8f, v = 63.0f;
    double r = d * g / (v * v);
    printf("%s\n",(r>1.0f)?"GT":"LE");
}

GCC编译器将405视为int,因此“double d = 405”实际上是“double d =(double)405”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM