首页 > 解决方案 > g++ 和双精度

问题描述

谁能向我解释为什么执行以下代码会返回 (1./3.)*3。等于1。?我假设四舍五入会阻止这种平等,但显然它不是那样工作的。有什么解释吗?

#include <iostream>

int main()
{
  double x = 1./3.;
  double y = 3.*x;
  std::cout.precision(30);
  std::cout << "x = " << x << std::endl;
  std::cout << "y = " << y << std::endl;
  if (y == 1.)
    std::cout << "Equality!" << std::endl;
  else
    std::cout << "Not equality." << std::endl;

  return 0;
}

它返回:

$ g++ test_dp.cpp -o test_dp.exe

$ ./test_dp.exe

x = 0.333333333333333314829616256247

y = 1

平等!

标签: c++

解决方案


假设您的double类型符合IEEE 754,则 1.0/3.0 的十六进制表示为3FD5555555555555. 这不完全是 1/3。它分解为:

  • 符号 = 0(即数字为正)
  • 有偏指数 = 3FD(即指数 = -2)
  • 53 位尾数 = 15555555555555(隐含前导1

乘以155555555555553 得到3FFFFFFFFFFFFF。要将其压缩为 53 位,我们必须丢弃最低有效位,这(在大多数环境的默认舍入模式下)意味着尾数“舍入为偶数”:尾随二进制01转到0,而尾随二进制11转到100(意味着可以传播进位)。在这种情况下,进位一直传播到最高有效位,留40000000000000在尾数中。我们希望第 52 位成为最高有效位,因此我们将其向下移动10000000000000并将指数增加 2。最终结果是3FF0000000000000,或者恰好是 1.0。

所以你的程序是正确的——一个不精确的结果乘以一个小整数会给你一个精确的结果。但人们可能会认为这更多是靠运气而不是判断:-)


推荐阅读