21

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

看下面的例子。

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
4

9 回答 9

17

a 的值float在转换为 a 时不会改变double。显示的数字有所不同,因为需要更多数字才能将double值与其相邻值区分开来,这是Java 文档所要求的。那是 的文档toString,从 的文档中引用(通过几个链接)println

的确切值125.32f是 125.3 1999969482421875。两个相邻的float值是 125.3 199920654296875和 125.3 2000732421875。观察到 125.32 比任何一个邻居都更接近 125.31999969482421875。因此,通过显示“125.32”,Java 显示了足够多的数字,以便从十进制数字转换回传递到float的值。floatprintln

125.3199996948242 1875的两个相邻double值为125.3199996948242 045391452847979962825775146484375和125.3199996948242 32960854715202003717422455 观察到 125.32 更接近后一个邻居而不是原始值 (125.31999969482421875)。因此,打印“125.32”不包含足够的数字来区分原始值。Java 必须打印更多的数字,以确保从显示的数字转换回复制传递到的值。
doubledoubleprintln

于 2013-07-07T02:07:36.177 回答
12
  1. 当您将 a 转换float为 adouble时,不会丢失信息。每个float都可以精确地表示为double.
  2. 另一方面,所打印的十进制表示都不是数字System.out.println的确切值。精确的十进制表示可能需要多达 760 个十进制数字。相反,System.out.println精确打印允许将十进制表示解析回原始floatdouble. 有更多double的 s,所以在打印一个时,System.out.println需要在表示变得明确之前打印更多的数字。
于 2013-07-07T01:19:58.087 回答
4

根据JLS的规定,从floatto的转换double是一种扩大的转换。扩大转换被定义为较小集合到其超集的单射映射。因此,表示的数字在从到转换后不会改变floatdouble

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

在您的更新中,您添加了一个示例,该示例应该证明该数字已更改。但是,它只显示数字的字符串表示形式发生了变化,这实际上是由于通过转换为double. 请注意,您的第一个输出只是第二个输出的四舍五入。由 指定 Double.toString

必须至少有一个数字来表示小数部分,除此之外,必须有尽可能多的数字,但仅能将参数值与 type 的相邻值区分开来double

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

于 2013-07-06T16:34:32.893 回答
3

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

当您将其转换为双精度时,值 125.31999969482421875 将变为双精度(此时无处可找到 125.32,它应该以 .32 结尾的信息完全丢失)当然可以表示正好是两倍。当您打印该双精度时,打印例程认为它的有效数字比实际数字多(但当然它不知道),因此它打印为 125.31999969482422,这是四舍五入到该精确双精度的最短小数(并且在该长度的所有小数中,它是最接近的)。

于 2013-07-06T17:49:45.303 回答
1

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

要查看浮点数的确切值,您可以使用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
于 2013-09-07T19:16:06.837 回答
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
于 2013-07-06T17:14:34.257 回答
0

它不会在 java 中工作,因为在 java 中默认情况下它会将实际值作为双精度值,如果我们声明一个浮点值而不像 123.45f 这样的浮点表示,默认情况下它会将它作为双精度值,并且会导致精度损失错误

于 2014-03-19T05:05:21.577 回答
0

由于将数值转换为 a Stringjava.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

于 2018-07-06T04:29:41.333 回答
-1

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

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

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

于 2013-07-06T16:34:41.587 回答