Java

时间:2015-09-15 20:49:23

标签: java ieee-754

我试图真正了解Java中的浮点数,双精度数和大数字数。我想知道每个类型中浮点数的确切表示方式,例如。浮点数使用2 ^,大小数使用10 ^加上缩放(32位)和未缩放值(任意精度)。

我使用所有三种类型汇总了简单的计算,并为每个类型进行了对话,结果相当混乱。我会理解为什么唯一正确的表示形式为浮点数的原因,以及为什么在转换为Double和BigDecimal时会出现拖尾不精确的情况。是否与二进制表示转换有关?无论如何这里是代码及其输出:

    // Float - 32b
    float a = 3.14f;
    float b = 3.100004f;
    float abAsAFloat = a + b;
    double abAsADouble = a + b;
    BigDecimal abAsABigDecimal = new BigDecimal(a + b);

    System.out.println("a + b as a float: " + abAsAFloat);
    System.out.println("a + b as a double: " + abAsADouble);
    System.out.println("a + b as a BigDecimal: " + abAsABigDecimal);

    // Double - 64b
    double c = 3.14;
    double d = 3.100004;

    double cdAsADouble = c + d;
    BigDecimal cdAsABigDecimal = new BigDecimal(c + d);

    System.out.println("c + d as a double: " + cdAsADouble);
    System.out.println("c + d as a BigDecimal: " + cdAsABigDecimal);

    // BigDecimal, arbitrary-precision, signBit*unscaledValue × 10^-scale
    BigDecimal e = new BigDecimal(3.14);
    BigDecimal f = new BigDecimal(3.100004);

    BigDecimal efAsABigDecimal = e.add(f);

    System.out.println("e + f: " + efAsABigDecimal);

    // Drawbacks. speed, memory, native value equality, no overloads for +/- et al
  

a + b作为浮点数:6.240004

     

a + b为双:6.240004062652588

     

a + b作为BigDecimal:6.240004062652587890625

     

c + d为双:6.240004000000001

     

c + d作为BigDecimal:   6.2400040000000007722746886429376900196075439453125

     

e + f:6.240004000000000328185478792875073850154876708984375

4 个答案:

答案 0 :(得分:1)

你无意中混淆了类型。例如:

BigDecimal e = new BigDecimal(3.14);
BigDecimal f = new BigDecimal(3.100004);

在这种情况下,您提供双打作为输入,因此e和f将具有双重残留。相反,使用这个:

BigDecimal e = new BigDecimal("3.14");
BigDecimal f = new BigDecimal("3.100004");

浮动输出似乎是最准确的,因为Java"知道"浮点数的精度有限,因此不会打印十五位数。

答案 1 :(得分:0)

对于特定情况

float可能看起来正确,但对于其他值来说,这也是错误的。请注意,当floatdouble转换为字符串时,只会打印尽可能多的数字,以便在该类型中获得正确的值 ;这意味着float可能会打印出“正确答案”,即使该表示隐藏了与double一样多的舍入错误。

BigDecimal的问题在于你没有正确使用它:你应该写new BigDecimal("3.14")而不是new BigDecimal(3.14),这会让double“弄乱它“BigDecimal之前有机会”修复它。“

有关表示的详细信息,https://en.wikipedia.org/wiki/Double-precision_floating-point_format对有用的图表有详尽的解释,但简短的解释是floatdouble表示数字为+/- 1 * 1. * 2 ^,其中float将尾数存储为22位,指数存储8位,double分别使用52和11位。

答案 2 :(得分:0)

当您转换为doubleBigDecimal时,它会转换为最接近的可表示值。当您转换为BigDecimal时,您实际上是首先转换为double,因为float没有直接转换。

通常你想使用doubleBigDecimal.valueOf(double)转换为BigDecimal这个方法假定一定程度的舍入以匹配打印时双倍的样子。

答案 3 :(得分:0)

阅读本文:Java Language Specification. Chapter 5. Conversions and Promotions

特别是, 5.6。数字促销

float a = 3.14f;
float b = 3.100004f;

double abAsADouble = a + b;

在这种情况下,首先将a添加到b,得到float结果,然后float将转换为double并分配。因此,与(double)a + b;

相比,它可能会失去精确度

同样的事情,当使用sum result作为构造函数

的参数时
new BigDecimal(a + b)

首先,float a添加到float b,给出float结果,然后将其转换为double,然后开始构建BigDecimal对象。

除非在结尾指定f,否则任何带小数点的数字常量都被认为是double,因此,当将常量传递给构造函数时:

new BigDecimal(3.100004);

Number以double存储,并以double精度传递给构造函数。要获得更高的精度,请改用String参数构造函数:

new BigDecimal(" 3.100004");

相关问题