问题描述
我惊讶地发现浮点到整数的转换是向上取整而不是截断小数部分。以下是一些使用 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时,不会发生截断/四舍五入,转换是准确的。
,使用精确数学计算,浮点值表示......
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",b);
double c = a * b;
double whole;
printf("c %.*e (r:%g)\n",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