Java:添加2个浮点数并将结果作为double

时间:2013-07-12 15:47:39

标签: java floating-point double

据我所知,双数字在内存中保留了64位。浮点数保留32位。

我尝试了以下内容:

double result=0.1f+0.3f;
System.out.println(result);

结果它打印0.4000000059604645。来自下一个存储器块的数据(32个第一位和下一个32位)?
还有别的吗?

1 个答案:

答案 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中具有无限的重复扩展,并且无法在floatdouble中完全表示。)float加法可能会引入更多错误(双关语)。从floatdouble的扩展范围很广,但系统会假设扩展的分辨率包含有意义的数据。

如果您希望使用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。如果您只需要外观整洁的输出,请使用格式说明符(例如,使用doubleSystem.out.printf())。

您还应该阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic