我正在学习IEE754,我对这些代码行感到困惑
double a = 0.2;
double b = 100.0;
double c = a * b;
我知道0.2不能通过2的幂来完美地表示,而100可以,但是我在c中得到了20的完美结果。
可视化构成这些值的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作为最大的幂。
所以,假设可视化器是正确的:
c
确切的结果?答案 0 :(得分:2)
IEEE算术规则中没有任何内容可以阻止舍入到最接近点击你想要的十进制计算的确切结果。
双文字100.0的确切值当然是100。
双字面值0.2的确切值是0.200000000000000011102230246251565404236316680908203125
他们的产品是20.000000000000001110223024625156540423631668090820312500
将此舍入舍入为20的舍入误差为1.110223024625156540423631668090820312500E-15
将其舍入为20.000000000000003552713678800500929355621337890625,最大双倍大于20的舍入误差为2.442490654175344388931989669799804687500E-15
由于向上舍入时的误差大于向下舍入到20的误差,因此双倍乘法的正确舍入到最接近的结果是20.0。舍入误差为2^-50 + 2^-52
,您的"丢失"权力2。
我使用Java程序进行计算,因为方便的BigDecimal类可以精确地表示所有有限双数,以及对它们的一些算法的结果,包括乘法。 Java双重算术遵循IEEE 754 64位二进制浮点数,即圆形到最近模式,这也是C双精度计算机的常用系统。
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