据我所知,双数在内存中保留 64 位。浮点数保留 32 位。
我尝试了以下方法:
double result=0.1f+0.3f;
System.out.println(result);
结果它打印出来0.4000000059604645
。是来自下一个内存块(32 个第一位和接下来的 32 个)的数据吗?
是别的吗?
据我所知,双数在内存中保留 64 位。浮点数保留 32 位。
我尝试了以下方法:
double result=0.1f+0.3f;
System.out.println(result);
结果它打印出来0.4000000059604645
。是来自下一个内存块(32 个第一位和接下来的 32 个)的数据吗?
是别的吗?
这一行:
double result=0.1f+0.3f;
首先将字符串“0.1”和“0.3”转换为值,然后使用规则float
将这两个值相加,最后将结果转换为a进行赋值。将浮点文字转换为值会引入舍入误差。(就像 1/3 在基数 10 中具有无限重复扩展一样,1/10 和 3/10 在基数 2 中具有无限重复扩展,并且不能在或中精确表示。)加法可能会引入更多错误(双关语)。从到的扩大扩展是准确的,但系统随后假定扩展的分辨率包含有意义的数据。float
float
double
float
float
double
float
float
double
如果您希望使用 的规则完成加法,则需要使用强制转换或使用文字double
将至少一个操作数转换为第一个操作数:double
double
double result=0.1f+0.3d;
在上面,0.3 将转换为double
全double
精度;0.1 将转换为 a float
,然后扩大为 a double
,从而导致double
值的舍入误差大于0.1d
. 大多数编译器会在编译时执行此操作。
使用 Oracle 的 javac 编译器(版本 1.7.0_11,默认设置)编译此代码:
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));
}
}
生成这样开头的字节码(使用 javap -c 反汇编):
ldc2_w #2 // double 0.4000000059604645d
dstore_1
ldc2_w #4 // double 0.4000000014901161d
dstore_3
ldc2_w #6 // double 0.4d
dstore 5
并产生这个输出:
dff=0.4000000059604645
dfd=0.4000000014901161
ddd=0.4
dff==dfd: false
dff==ddd: false
dfd==ddd: false
ddd==0.4: true
最后一行显示“true”,因为 0.1d 和 0.3d 的表示误差以及加法的舍入误差(如果有)碰巧以与 0.4d 的表示误差完全匹配的方式组合。0.3
如果您更改为0.2
(并更改10*ddd==4
为10*ddd==3
) ,事情就不会那么好了。
一般来说,无论您使用float
或double
,都会有一些舍入误差,结果不太可能准确。为了提高精度,您应该使用BigDecimal
. 如果您只需要看起来干净的输出,请使用格式说明符(例如,withSystem.out.printf()
或String.format()
)。
您还应该阅读每个计算机科学家应该了解的关于浮点运算的知识。