据我所知,双数字在内存中保留了64位。浮点数保留32位。
我尝试了以下内容:
double result=0.1f+0.3f;
System.out.println(result);
结果它打印0.4000000059604645
。来自下一个存储器块的数据(32个第一位和下一个32位)?
还有别的吗?
答案 0 :(得分:7)
这一行:
double result=0.1f+0.3f;
首先将字符串“0.1”和“0.3”转换为float
值,然后使用float
的规则添加两个float
值,最后将结果转换为double
进行作业。浮点文字到float
值的转换引入了舍入误差。 (就像1/3在基数10中具有无限的重复扩展,1/10和3/10在基数2中具有无限的重复扩展,并且无法在float
或double
中完全表示。)float
加法可能会引入更多错误(双关语)。从float
到double
的扩展范围很广,但系统会假设扩展的分辨率包含有意义的数据。
如果您希望使用double
的规则完成添加,则需要先将至少一个操作数转换为double
,或者使用强制转换或使用{{ 1}}文字:
double
在上面,0.3将被转换为double result=0.1f+0.3d;
到完全double
精度; 0.1将转换为double
,然后加宽为float
,导致double
值的舍入误差比double
更大。大多数编译器都会在编译时执行此操作。
使用Oracle的javac编译器(版本1.7.0_11,默认设置)编译此代码:
0.1d
生成如下所示的字节码(使用javap -c进行反汇编):
public class Foo1 {
public static void main(String[] args) {
double dff = 0.1f + 0.3f;
double dfd = 0.1f + 0.3d;
double ddd = 0.1d + 0.3d;
System.out.println("dff=" + dff);
System.out.println("dfd=" + dfd);
System.out.println("ddd=" + ddd);
System.out.println("dff==dfd: " + (dff == dfd));
System.out.println("dff==ddd: " + (dff == ddd));
System.out.println("dfd==ddd: " + (dfd == ddd));
System.out.println("ddd==0.4: " + (10*ddd == 4));
}
}
并生成此输出:
ldc2_w #2 // double 0.4000000059604645d
dstore_1
ldc2_w #4 // double 0.4000000014901161d
dstore_3
ldc2_w #6 // double 0.4d
dstore 5
最后一行显示“true”,因为0.1d和0.3d的表示错误以及加法的舍入误差(如果有的话)碰巧以与0.4d的表示错误完全匹配的方式组合。如果您将dff=0.4000000059604645
dfd=0.4000000014901161
ddd=0.4
dff==dfd: false
dff==ddd: false
dfd==ddd: false
ddd==0.4: true
更改为0.3
(并将0.2
更改为10*ddd==4
),情况就不会那么好。
一般情况下,无论您使用10*ddd==3
还是float
,都会出现一些舍入错误,结果不太准确。为了扩展精度,您应该使用BigDecimal
。如果您只需要外观整洁的输出,请使用格式说明符(例如,使用double
或System.out.printf()
)。
您还应该阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic。