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

时间:2013-09-17 03:13:00

标签: c++ floating-point g++ ieee-754 floating-point-precision

我的代码很简单

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)
  • g ++ on ubuntu: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 ++-mingw-3.4.2获得 LE

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

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

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

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

5 个答案:

答案 0 :(得分:4)

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

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

9.7999999999999989341858963598497211933135986328125
9.800000000000000710542735760100185871124267578125

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

答案 1 :(得分:1)

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

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

通常,在英特尔处理器上,编译器通过以下两种方式之一评估d * g:使用double算术或使用long double和英特尔的80位浮点格式。使用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;

答案 2 :(得分:0)

回答你的问题:由于表达式应该评估为“相等”,因此测试r>1.0应为false,打印结果应为"LE"

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

答案 3 :(得分:0)

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

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

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

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

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

答案 4 :(得分:-1)

我测试了代码,这应该返回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”。