3

我有一个问题来自另一个关于浮点数精度的问题。

现在,我知道浮点数不能总是准确地表示,因此它们被存储为可以表示的最接近的浮点数。

我的问题实际上是关于 和 的表示形式的float差异double

这个问题从何而来?

假设我这样做:

System.out.println(.475d+.075d);

那么输出不会是0.55但是0.549999(在我的机器上)

但是,当我这样做时:

System.out.println(.475f+.075f);

我得到了正确的答案,即0.55(对我来说有点意外)

到目前为止,我的印象是double精度更高(双精度更高,小数位数更长float。因此,如果一个 double 不能精确表示,那么它的等效浮点表示也将不准确地存储。

然而,我得到的结果对我来说有点令人不安。如果出现以下情况,我会感到困惑:

  1. 我对什么precision意思有不正确的理解?
  2. float并且double表示方式不同,除了 double 具有更多位的事实之外?
4

3 回答 3

8

可以表示为 a 的数字float也可以表示为double

你读到的只是格式化的输出,你没有读到实际的二进制表示。

System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(.475d + .075d)));
// 11111111100001100110011001100110011001100110011001100110011001
System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(.475f + .075f)));
// 111111000011001100110011001101

double d = .475d + .075d;
System.out.println(d);
// 0.5499999999999999
System.out.println((float)d);
// 0.55 (as expected)
System.out.println((double)(float)d);
// 0.550000011920929

System.out.println( .475f + .075f == 0.550000011920929d);
// true
于 2012-06-08T22:32:57.017 回答
5

精度只是意味着更多的位。不能表示为 a 的数字float 可能具有作为 a 的精确表示double,但这些案例的数量相对于可能案例的总数是无限小的。

对于像这样的简单情况0.1,无论可用的位数是多少,它都不能表示为固定长度的浮点数。这与说诸如 1/7 之类的小数不能用十进制精确表示是一样的,无论您允许使用多少位数(只要位数是有限的)。您可以将其近似为 0.142857142857142857... 一遍又一遍地重复,但无论您持续多久,您都永远无法准确地写出它。

相反,如果一个数字可以精确地表示为 a float,那么它也可以精确地表示为 a double。双精度数具有更大的指数范围和更多的尾数位。

对于您的示例,明显差异的原因是float0.475 与其浮点表示之间的差异在“正确”方向,因此当发生截断时,它会按照您的预期进行。当增加可用的精度时,表示“更接近”0.475,但现在在相反的一侧。作为一个粗略的例子,假设最接近的可能浮点数是 0.475006,但在双精度中,最接近的可能值是 0.474999。这会给你你看到的结果。

编辑:这是一个快速实验的结果:

public class Test {

    public static void main(String[] args)
    {
        float  f = 0.475f;
        double d = 0.475d;

        System.out.printf("%20.16f", f);
        System.out.printf("%20.16f", d);
    }
}

输出:

  0.4749999940395355  0.4750000000000000

这意味着数字 0.475 的浮点表示,如果你有大量的位,将只比 0.475 小一点。这可以在双重表示中看到。然而,第一个“错误”位出现在右边,当被截断以适应 a 时float,它恰好是 0.475。这纯粹是个意外。

于 2012-06-08T22:23:56.977 回答
1

如果有人认为浮点类型实际上代表值的范围,而不是离散值(例如0.1f,不代表 13421773/134217728,而是“介于 13421772.5/134217728 和 13421773.5/134217728 之间的某个值”),从doubleto 的转换float通常是准确的, 而从floatto 的转换double通常不会。不幸的是,Java 允许隐式执行通常不准确的转换,同时需要在通常准确的方向上进行类型转换。

对于 type 的每个值,都float存在一个 type 的值,其范围以's 范围double的中心为中心。float这并不意味着它double是浮点值的准确表示。例如,转换0.1fdouble产生一个值,意思是“介于 13421772.9999999/134217728 和 13421773.0000001/134217728 之间”,该值偏离隐含公差超过一百万倍。

对于几乎每个 type 值double,都存在一个 type 值,float其范围完全包括double. 唯一的例外是其范围恰好位于两个值之间的边界上的float值。将这些值转换为float需要系统选择一个范围或另一个范围;如果系统在实际表示低于其范围中心的数字时四舍五入double,反之亦然,则 的范围float不会完全包含的范围double。但实际上,这不是问题,因为这意味着不是floatdouble代表一个范围,例如(13421772.5/134217728 到 13421773.5/134217728),它将代表一个范围,例如(13421772.4999999/134217728 到 13421773.5000001/134217728)。与 a floatto doublecast 造成的可怕的不精确相比,那微小的不精确不算什么。

顺便说一句,返回到您正在使用的特定数字,当您以浮点数进行计算时,计算结果为:

0.075f = 20132660±½ / 268435456
0.475f = 31876710±½ / 67108864
总和 = 18454938±½ / 33554432

换句话说,总和表示大约在 0.54999999701 和 0.55000002682 之间的某个数字。最自然的表示是 0.55(因为实际值可能大于或小于该值,所以额外的数字将毫无意义)。

于 2012-06-08T22:53:56.213 回答