我试图真正了解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
答案 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
可能看起来正确,但对于其他值来说,这也是错误的。请注意,当float
和double
转换为字符串时,只会打印尽可能多的数字,以便在该类型中获得正确的值 ;这意味着float
可能会打印出“正确答案”,即使该表示隐藏了与double
一样多的舍入错误。
BigDecimal
的问题在于你没有正确使用它:你应该写new BigDecimal("3.14")
而不是new BigDecimal(3.14)
,这会让double
“弄乱它“BigDecimal
之前有机会”修复它。“
有关表示的详细信息,https://en.wikipedia.org/wiki/Double-precision_floating-point_format对有用的图表有详尽的解释,但简短的解释是float
和double
表示数字为+/- 1 * 1. * 2 ^,其中float
将尾数存储为22位,指数存储8位,double
分别使用52和11位。
答案 2 :(得分:0)
当您转换为double
或BigDecimal
时,它会转换为最接近的可表示值。当您转换为BigDecimal时,您实际上是首先转换为double
,因为float
没有直接转换。
通常你想使用double
从BigDecimal.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");