c++ - 我在 IEEE754 乘法中失去了 2 的幂

标签 c++ c double precision ieee-754

我正在研究 IEE754,我对这些代码行有点困惑

double a = 0.2;
double b = 100.0;
double c = a * b;

我知道 0.2 不能通过 2 的幂完美表示,而 100 可以,但我得到了 20 作为 c 的完美结果。

可视化构成这些值的 2 的幂(我正在使用一个简单的 js 可视化工具:http://bartaz.github.io/ieee754-visualization/)我看到 0.2 以

2^-3 + 2^-4 + 2^-7...

和100个

2^6 + 2^5 + 2^2

现在回答我的问题:这是 20,又名 c 的样子

2^4 + 2^2
^^^

什么? 2^4 是从哪里来的?如果我用数学方法将 0.2 的所有项乘以 100 的所有项,我将得到 2^3 作为最大幂。

因此,假设可视化工具是正确的:

  • 2^4 从何而来?
  • 如果 0.2 从一开始就是不精确的,为什么 0.2 乘以 100 不会有任何精度损失?为什么 c 是准确的结果?

最佳答案

IEEE 算术规则中没有任何内容可以阻止舍入到最接近的值达到您想要执行的小数计算的确切结果。

双字面量 100.0 的准确值当然是 100。

双字面量0.2的准确值为0.200000000000000011102230246251565404236316680908203125

他们的产品是 20.000000000000001110223024625156540423631668090820312500

将其向下舍入为 20 的舍入误差为 1.110223024625156540423631668090820312500E-15

四舍五入到 20.000000000000003552713678800500929355621337890625 的舍入误差,即大于 20 的最小 double ,将是 2.442490654175344388931989669799804687500E-15

由于四舍五入的误差大于四舍五入到 20 的误差,因此双倍乘法的正确舍入到最近的结果是 20.0。舍入误差为 2^-50 + 2^-52,即您“丢失”的 2 的幂。

我使用 Java 程序进行计算,因为方便的 BigDecimal 类可以精确表示所有有限 double ,以及对它们进行的一些算术结果,包括乘法。 Java double 算法遵循 IEEE 754 64 位二进制 float 舍入到最近模式,这也是 C double 的常用系统。

import java.math.BigDecimal;

public class Test {
  public static void main(String[] args) {
    double a = 0.2;
    double b = 100.0;
    double c = a * b;
    display(a);
    display(b);
    display(c);
    BigDecimal exactProduct = new BigDecimal(a).multiply(new BigDecimal(b));
    System.out.println(exactProduct);
    BigDecimal down = new BigDecimal(20.0);
    System.out.println(down);
    BigDecimal up = new BigDecimal(Math.nextUp(20.0));
    System.out.println(up);
    System.out.println("Round down error "+exactProduct.subtract(down));
    System.out.println("Round up error "+up.subtract(exactProduct));
  }
  private static void display(double in){
    System.out.println(new BigDecimal(in));
  }
}

输出:

0.200000000000000011102230246251565404236316680908203125
100
20
20.000000000000001110223024625156540423631668090820312500
20
20.000000000000003552713678800500929355621337890625
Round down error 1.110223024625156540423631668090820312500E-15
Round up error 2.442490654175344388931989669799804687500E-15

关于c++ - 我在 IEEE754 乘法中失去了 2 的幂,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40919288/

相关文章:

c - strlen(string) 和 strlen(*string) 之间的区别

c++ - 在包含 float 的结构上使用 memset()

c++ - 切换模板类型

c++ - 如何在继续更新和绘制游戏项目的同时检查键盘输入

c++ - 逻辑流程不正确?获取数独游戏坐标的函数

c++ - 转换(const char*)var出错

java - 以 double 传递浮点值时的 double

c++ - "Standard library"用于使用 Make 构建 C/C++ 项目

c - 我创建这个数组时分配的内存在哪里? (C)

c++ - 从有符号字符转换为无符号字符然后再转换回来?