为什么从float转换为double会改变这个值?

时间:2013-07-06 16:29:34

标签: java floating-point double precision ieee-754

我一直试图找出原因,但我不能。 有人能帮助我吗?

请看以下示例。

float f = 125.32f;
System.out.println("value of f = " + f);
double d = (double) 125.32f; 
System.out.println("value of d = " + d);

这是输出:

value of f = 125.32
value of d = 125.31999969482422

9 个答案:

答案 0 :(得分:14)

转换为float时,double的值不会更改。显示的数字存在差异,因为需要更多的数字来区分double值与其邻居,即required by the Java documentation。这是toString的文档,该文档是通过println的文档引用的(通过多个链接)。

125.32f的确切值为125.31999969482421875。两个相邻的float值为125.3199920654296875和125.32000732421875。观察125.32比任何一个邻居更接近125.31999969482421875。因此,通过显示“125.32”,Java已显示足够的数字,以便从十进制数字转换回float再现传递给float的{​​{1}}的值。

125.31999969482421875的两个相邻println值是125.3199996948242045391452847979962825775146484375和125.3199996948242329608547152020037174224853515625。观察到125.32比后者更接近于原始值。因此,打印“125.32”不包含足以区分原始值的数字。 Java必须打印更多数字,以确保从显示的数字转换回double再现传递给double的{​​{1}}的值。

答案 1 :(得分:12)

  1. float转换为double时,不会丢失任何信息。每个float都可以完全表示为double
  2. 另一方面,System.out.println打印的十进制表示都不是该数字的确切值。精确的十进制表示可能需要最多约760个十进制数字。相反,System.out.println会精确打印允许将十进制表示形式解析回原始floatdouble的小数位数。还有更多double s,因此在打印时,System.out.println需要在表示变得明确之前打印更多数字。

答案 2 :(得分:4)

floatdouble的转换是扩展转化,为specified by the JLS。扩展转换被定义为较小集合到其超集的内射映射。因此,从float转换为double后,代表的数字不会更改

有关更新后问题的更多信息

在您的更新中,您添加了一个示例,该示例应该证明该数字已更改。但是,它仅显示数字的字符串表示已更改,实际上它由于通过转换为double而获得的额外精度。请注意,您的第一个输出只是第二个输出的四舍五入。由Double.toString指定,

  

必须至少有一个数字来表示小数部分,并且除此之外必须有多个数字,但只需要更多的数字来唯一地将参数值与类型double的相邻值区分开来。< / p>

由于类型double中的相邻值比float中的相邻值更近,因此需要更多数字才能符合该裁定。

答案 3 :(得分:3)

最接近125.32的32位IEEE-754浮点数实际上是125.31999969482421875。非常接近,但不完全(因为0.32是以二进制重复)。

当你把它转换为double时,它的值是125.31999969482421875,它将成为一个双精度数(125.32在这一点上无处可寻,它应该真正结束的信息.32完全丢失)当然可以用双精确表示。当你打印那个双,打印例程认为它有比实际更多的有效数字(但当然它不知道),所以它打印到125.31999969482422,这是最小的十进制,舍入到那个精确的双(和该长度的所有小数,它是最接近的。)

答案 4 :(得分:1)

浮点数的精度问题实际上与语言无关,所以我将在我的解释中使用MATLAB。

您看到差异的原因是某些数字在固定位数中无法准确表示。以0.1为例:

>> format hex

>> double(0.1)
ans =
   3fb999999999999a

>> double(single(0.1))
ans =
   3fb99999a0000000

因此,当您将其作为双精度浮点数转换时,单精度中0.1近似值的误差会变大。如果直接以双精度开始,结果与其近似值不同。

>> double(single(0.1)) - double(0.1)
ans =
     1.490116113833651e-09

答案 5 :(得分:1)

正如已经解释的那样,所有浮点数都可以精确地表示为double,问题的原因是System.out.println在显示floatdouble的值时执行了一些舍入,但在这两种情况下,舍入方法都不一样。

要查看浮动的确切值,您可以使用BigDecimal

float f = 125.32f;
System.out.println("value of f = " + new BigDecimal(f));
double d = (double) 125.32f;
System.out.println("value of d = " + new BigDecimal(d));

输出:

value of f = 125.31999969482421875
value of d = 125.31999969482421875

答案 6 :(得分:0)

它在java中不起作用,因为在java中默认情况下它会将实际值作为double,如果我们声明一个没有float表示的float值 喜欢 123.45f 默认情况下,它会将其视为double,并且会导致错误,因为精度损失

答案 7 :(得分:0)

由于将数值转换为String(相应地为java.lang.Float#toString(float)java.lang.Double#toString(double)而将实际值保持不变的方法的契约,因此值的表示形式发生了变化。在Javadoc中,上述两种方法都有一个共同的部分,它们详细说明了值的String表示形式的要求:

  

必须至少有一位数字来表示小数部分,并且除此以外,还必须要有多少个数字才能唯一区分参数值与相邻值

为说明两种类型值的重要部分的相似性,可以运行以下代码段:

package com.my.sandbox.numbers;

public class FloatToDoubleConversion {

    public static void main(String[] args) {
        float f = 125.32f;
        floatToBits(f);
        double d = (double) f;
        doubleToBits(d);
    }

    private static void floatToBits(float floatValue) {
        System.out.println();
        System.out.println("Float.");
        System.out.println("String representation of float: " + floatValue);
        int bits = Float.floatToIntBits(floatValue);
        int sign = bits >>> 31;
        int exponent = (bits >>> 23 & ((1 << 8) - 1)) - ((1 << 7) - 1);
        int mantissa = bits & ((1 << 23) - 1);
        System.out.println("Bytes: " + Long.toBinaryString(Float.floatToIntBits(floatValue)));
        System.out.println("Sign: " + Long.toBinaryString(sign));
        System.out.println("Exponent: " + Long.toBinaryString(exponent));
        System.out.println("Mantissa: " + Long.toBinaryString(mantissa));
        System.out.println("Back from parts: " + Float.intBitsToFloat((sign << 31) | (exponent + ((1 << 7) - 1)) << 23 | mantissa));
        System.out.println(10D);
    }

    private static void doubleToBits(double doubleValue) {
        System.out.println();
        System.out.println("Double.");
        System.out.println("String representation of double: " + doubleValue);
        long bits = Double.doubleToLongBits(doubleValue);
        long sign = bits >>> 63;
        long exponent = (bits >>> 52 & ((1 << 11) - 1)) - ((1 << 10) - 1);
        long mantissa = bits & ((1L << 52) - 1);
        System.out.println("Bytes: " + Long.toBinaryString(Double.doubleToLongBits(doubleValue)));
        System.out.println("Sign: " + Long.toBinaryString(sign));
        System.out.println("Exponent: " + Long.toBinaryString(exponent));
        System.out.println("Mantissa: " + Long.toBinaryString(mantissa));
        System.out.println("Back from parts: " + Double.longBitsToDouble((sign << 63) | (exponent + ((1 << 10) - 1)) << 52 | mantissa));
    }
}

在我的环境中,输出为:

Float.
String representation of float: 125.32
Bytes: 1000010111110101010001111010111
Sign: 0
Exponent: 110
Mantissa: 11110101010001111010111
Back from parts: 125.32

Double.
String representation of double: 125.31999969482422
Bytes: 100000001011111010101000111101011100000000000000000000000000000
Sign: 0
Exponent: 110
Mantissa: 1111010101000111101011100000000000000000000000000000
Back from parts: 125.31999969482422

这样,您可以看到值的符号,指数相同,而其尾数被扩展时保留其有效部分(11110101010001111010111)完全相同。

使用的浮点数部分提取逻辑:12

答案 8 :(得分:-1)

两者都是微软所称的“近似数字数据类型”。

有一个原因。一个浮点数具有7位数的精度和一个双15。但我已经看到它发生了很多次8.0 - 1.0 - 6.999999999。这是因为它们不能保证完全代表十进制数。

如果您需要绝对的,不变的精度,请使用小数或整数类型。