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

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

请看以下示例。

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

这是输出:

值f = 125.32

值d = 125.31999969482422

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

转换为double时, float的值不会更改。 显示的数字有所不同,因为需要更多的数字来区分double值和它的邻居,这是Java文档所要求的 。 这是toString的文档,它通过println的文档引用(通过几个链接)。

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

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

floatdouble转换扩展转换 , 由JLS指定 。 扩展转换被定义为较小集合到其超集的内射映射。 因此,从float转换为double后,所表示的数字不会改变

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

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

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

由于double类型中的相邻值比float的相邻值更接近,因此需要更多数字来符合该规则。

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

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

浮点数的精度问题实际上与语言无关,所以我将在我的解释中使用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 

如前所述,所有浮点数都可以精确地表示为double,问题的原因是System.out.println在显示floatdouble的值时执行一些舍入,但舍入方法在两种情况下都不相同。

要查看float的确切值,可以使用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 

它不会在java中工作,因为在默认情况下,它会将实际值作为double,如果我们声明一个没有浮动表示的浮点值,如123.45f,默认情况下会将它作为double,它将导致错误,因为精度损失

由于将数值转换为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 )完全相同。

浮点数部分的使用提取逻辑: 1和2 。

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

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

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