Java中浮点类型的转换和计算
我试图在Java中学习更多关于浮点、双精度和大小数的知识。我想确切地知道浮点数在每种类型中是如何表示的,例如,浮点数使用2^,大小数使用10^加上刻度(32位)和无刻度值(任意精度) 我用这三种类型组合了简单的计算,并对每种类型进行了对话,结果相当混乱。我希望得到一些提示,说明为什么唯一正确的表示法是float,以及为什么转换为Double和BigDecimal时会出现尾部不精确。这与二进制表示转换有关吗?无论如何,下面是代码及其输出:Java中浮点类型的转换和计算,java,ieee-754,Java,Ieee 754,我试图在Java中学习更多关于浮点、双精度和大小数的知识。我想确切地知道浮点数在每种类型中是如何表示的,例如,浮点数使用2^,大小数使用10^加上刻度(32位)和无刻度值(任意精度) 我用这三种类型组合了简单的计算,并对每种类型进行了对话,结果相当混乱。我希望得到一些提示,说明为什么唯一正确的表示法是float,以及为什么转换为Double和BigDecimal时会出现尾部不精确。这与二进制表示转换有关吗?无论如何,下面是代码及其输出: // Float - 32b float
// 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作为大十进制:6.240004062652587890625
双精度c+d:6.24000400000001
c+d作为一个大十进制:
6.24000400000000772274688642976900196075439453125
e+f:6.24000400000000328185478792875073850154876708984375
float
对于这种特殊情况可能看起来是正确的,但对于其他值来说也是错误的。请注意,当float
和double
转换为字符串时,仅打印获得该类型中正确值所需的位数;这意味着float
可以打印“正确答案”,即使该表示法隐藏的舍入误差与double
一样大
BigDecimal
的问题在于您没有正确地使用它:您应该编写新的BigDecimal(“3.14”)
而不是新的BigDecimal(3.14)
,这使得double
能够在BigDecimal
有机会“修复”之前“搞糟”
对于表示的详细信息,我们用有用的图表进行了详尽的解释,但简短的解释是,
float
和double
将数字表示为+/-1*1。*2^,其中float
存储22位尾数和8位指数,double
分别使用52位和11位。float
对于这种特殊情况可能看起来正确,但对于其他值也一样错误。请注意,当float
和double
转换为字符串时,仅打印获得该类型中正确值所需的位数;这意味着float
可以打印“正确答案”,即使该表示法隐藏的舍入误差与double
一样大
BigDecimal
的问题在于您没有正确地使用它:您应该编写新的BigDecimal(“3.14”)
而不是新的BigDecimal(3.14)
,这使得double
能够在BigDecimal
有机会“修复”之前“搞糟”
对于表示的详细信息,我们用有用的图表进行了详尽的解释,但简短的解释是,
float
和double
将数字表示为+/-1*1。*2^,其中float
存储22位尾数和8位指数,double
分别使用52位和11位。您无意中混合了类型。例如:
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“知道”浮点的精度有限,所以它不会打印15位数字。您无意中混合了类型。例如:
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“知道”浮点的精度有限,所以它不会打印15位数字。当您转换为
double
或BigDecimal
时,它会转换为最接近的可表示值。当您转换为BigDecimal时,实际上是先转换为double
,因为没有从float
直接转换
通常,您希望使用
BigDecimal.valueOf(double)从double
转换为BigDecimal
此方法假定一定程度的舍入,以匹配打印时的双精度值。当您转换为double
或BigDecimal
时,它将转换为最接近的可表示值。当您转换为BigDecimal时,实际上是先转换为double
,因为没有从float
直接转换
通常,您希望使用BigDecimal将double
转换为BigDecimal。valueOf(double)
此方法假定一定程度的舍入,以匹配打印时的双精度值。请阅读以下内容:
特别是,5.6。数字促销
i、 e
在这种情况下,首先将a添加到b中,给出一个float
结果,然后float
将转换为double
,并进行赋值。因此,与(double)a+b
相比,它可能会失去精度
同样,当使用sum result作为构造函数的参数时
new BigDecimal(a + b)
首先,将float a
添加到float b
,给出一个float
结果,然后将其转换为double,然后开始构造BigDecimal对象
除非您在末尾指定了f
,否则任何带小数点的数值常量都被视为双精度
,因此,将常量传递给构造函数时:
新的BigDecimal(3.100004)
数字存储为double
,并作为double
精度传递给构造函数。要获得更高的精度,请改用String
参数构造函数:
新的BigDecimal(“3.100004”) 读一下:
特别是,5.6。数字促销