微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

浮点到整数转换向上舍入而不是截断

如何解决浮点到整数转换向上舍入而不是截断

我惊讶地发现浮点到整数的转换是向上取整而不是截断小数部分。以下是一些使用 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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。