浮点数到整数的转换四舍五入而不是截断

Floating-point-to-integer conversion rounding up instead of truncating

我惊讶地发现浮点数到整数的转换是四舍五入而不是截断小数部分。下面是一些示例代码,使用 Clang 编译,重现了该行为:

double a = 1.12;  // 1.1200000000000001 * 2^0
double b = 1024LL * 1024 * 1024 * 1024 * 1024;  // 1 * 2^50
double c = a * b;  // 1.1200000000000001 * 2^50
long long d = c;  // 1261007895663739

使用精确数学,浮点值表示

1.1200000000000001 * 2^50 = 1261007895663738.9925899906842624

由于截断,我原以为生成的整数是 1261007895663738,但实际上是 1261007895663739。为什么?

假设 IEEE 754 双精度,1.12 正好是

1.12000000000000010658141036401502788066864013671875

写成二进制,它的有效位数正好是:

1.0001111010111000010100011110101110000101000111101100

请注意最后两个零是有意为之的,因为它是双精度得到的(小数分隔符前 1 位,加上 52 位小数位)。

因此,如果您移动 50 个位置,您将得到一个整数值

100011110101110000101000111101011100001010001111011.00

或十进制

1261007895663739

转换为long long时,没有发生truncation/rounding,转换是正确的。

Using exact math, the floating-point value represents ...

a 不完全是 1.12 因为 0.12 不是 dyadic.

// `a` not exactly 1.12 
double a = 1.12;  // 1.1200000000000001 * 2^0

附近的 double 个值:

1.11999999999999988...  Next closest double
1.12                    Code
1.12000000000000011...  Closest double
1.12000000000000033...

相反,让我们更接近真实值。

#include <stdio.h>
#include <float.h>

int main() {
  double a = 1.12;  // 1.1200000000000001 * 2^0
  double b = 1024LL * 1024 * 1024 * 1024 * 1024;  // 1 * 2^50
  int prec = DBL_DECIMAL_DIG;
  printf("a %.*e\n", prec, a);
  printf("b %.*e\n", prec, b);

  double c = a * b;
  double whole;
  printf("c %.*e (r:%g)\n", prec, c, modf(c, &whole));
  long long d = (long long) c;
  printf("d %lld\n", d);
}

输出

a 1.12000000000000011e+00
b 1.12589990684262400e+15
c 1.26100789566373900e+15 (r:0)
d 1261007895663739