Python C ++浮点数学表示错误

时间:2019-01-29 15:25:27

标签: python c++ floating-point

在C ++中,

double x = 1.0; 
double y = x / 3.0; 
if (x == y * 3.0)
  cout << " They are equal!" ; 
else
  cout << " They are NOT equal." ;

将打印

‘They are NOT equal.’ 

如预期的那样,由于1/3作为有限尺寸尾数的(二进制)数的不精确表示。 但是在Python中(在https://repl.it/repls/MessyJaggedMouse上),

x = 1.0
y = x / 3.0
x == y * 3.0

打印

True

Python何时以及为何偏离上述预期行为?编辑:Python不会离开-请在下面查看我的答案。

2 个答案:

答案 0 :(得分:6)

仅当我在C语言中使用x87数学时,这种情况才会发生。

正确舍入,使用IEEE 754双重算术,您将得到 true

但是,如果您以较高的精度计算中间值,则会得到 false 。不需要C来以64位精度计算所有中间值,并且在具有x87浮点指令的32位x86处理器上,最终将使用较大的80位浮点类型作为中间值。根据启用的优化设置和编译器的详细信息,计算将以不同的方式进行(使用不同的中间值),您将获得略有不同的结果。

#include <cstdio>

int main() {
  double x = 1.0;
  double y = x / 3.0;
  std::printf("x == y * 3.0: %s\n", x == y * 3.0 ? "true" : "false");
  return 0;
}

在GCC中,如果您使用false进行编译,则会看到-mfpmath=387 -O0。如果不使用true进行编译(在AMD64上默认为SSE),或者如果使用-mfpmath=387进行编译,则可以看到-O2

您可以在GodBolt上使用x87指令查看其编译方式:https://godbolt.org/z/rf1Rir-尝试添加-O2或摆脱-mfpmath=387,以查看它如何影响所生成的代码。

请注意,(1.0 / 3.0) * 3.0 == 1.0有点巧合。您可以在Python中测试以下代码,例如:

1.0 / 49.0 * 49.0 == 1.0

这应该给False

答案 1 :(得分:0)

Python的浮子'使用double精度52位的尾数:

> (1/3).hex()
‘0x1.5555555555555p-2’ # 13 5’s, 13*4==52

这是在二进制1/3:

1/3 == 0.0101010101...

52是存储偶数,因此尾数(第一1后部分)

010101...

以52位表示,因此以...0101结尾。因此,ieee 754舍入不会改变52位尾数,而是乘以3或二进制11进行倍增,得到0.11111...并舍入到1。因此

1.0 / 3.0 * 3.0 == 1.0

在ieee 754中,因此在@Dietrich指出的python和c ++ double s中也是如此。

下面的代码允许探索为float S的有23个比特(在我的系统)的故事,这是一个奇数... 请注意,四舍五入为23位会恢复为1,但是在float之类的语句中从double3.0 * x == 1的自动提升将阻止四舍五入为1。

include <iostream> using namespace std;

int main() {
  float x = 1./3, y;
  cout << hexfloat; 
  cout << x << endl;       // 1./3. ieee rounded to 23 bits 
  cout << x * 3.0 << endl; // that times 3.0 exact, ie, promoted to double 
  cout << (double)x * 3. << endl; // same 
  cout << x * (float)3.0 << endl; // remains 23 bits, rounded to 1 
  y = 3.0 * x;          // 0x1.0000008p+0 static_cast to 23 bit float    
  cout << y << endl;    // again yields 1 

  cout << boolalpha;    // Now in terms of equalities:
  cout << (3.0*x==1.) << endl; 
  cout << ((float)3.*x==1.) << endl; 
  cout << (y==1.); return 0;
}

收益

0x1.555556p-2     // rounded to 1.0101...01011 
0x1.0000008p+0    // that times 3.0
0x1.0000008p+0 
0x1p+0            // now rounds to 1.00...
0x1p+0 
false
true
true

锻炼; Tibial:探索49.0。 :)