我在Java中有一个简单的划分:
float f = 19.7f/100; System.out.println(f); // 0.19700001 double d = 19.7/100; System.out.println(d); // 0.19699999999999998
为什么会发生这种情况?
5lhxktic1#
这是有史以来最常被问到的问题之一,所以我在这里提出几点。1.计算机只能表示有限位数,因此在存储数字和随后对数字进行除法运算时,必须进行四舍五入,这种四舍五入自然会产生误差,但如果你只想要精度为3位的数字,那么这些误差对你来说就不重要了。1.舍入的行为有点不可预测,因为计算机是以二进制存储数字的,所以19.7是一个终止十进制数,同样的数字是一个二进制的循环十进制数--10011.1011001100110011001100110011......因此,您可以看到,在任意点舍入将产生无法从终止十进制表达式预测的行为。
oxf4rvwz2#
Mystical给出的链接是必读的,但是有点厚。尝试this site,获得一个更友好的入门版本。tl;dr是指浮点运算总是要舍入的,而双精度,由于精度更高,舍入的方式和浮点数不同,有点像55舍入到最接近的10等于60,但是舍入到最接近的100等于100。在这种情况下,你不能用浮点数或双精度数精确地表示十进制数0.197(或19.7),所以每一个都给出了它所能表示的最接近该值的数字,双精度数可以更接近一点,因为它有更高的精度。
e5nszbig3#
这不是除法的问题,问题是由于精度损失导致1.7f!= 1.7。我们可以看看值的位表示
float f = 19.7f; double d = 19.7; System.out.println(Double.doubleToLongBits(f)); System.out.println(Double.doubleToLongBits(d));
输出
4626238274938077184 4626238274723328819
8nuwlpux4#
Java使用IEEE754浮点数来处理它的浮点数和双精度数。这个标准被设计来伊势不能被用来精确地表示基数为10的基数为2的数。参见这里http://en.wikipedia.org/wiki/IEEE_floating_point。下面的例子并不完全是标准的,只是让你了解一下为什么基2浮点不适合其他基。
base2 = base10 0001 = 0001 -> from 0*8 + 0*4 + 0*2 + 1*1 0010 = 0002 -> from 0*8 + 0*4 + 1*2 + 0*1 0011 = 0003 -> from 0*8 + 0*4 + 1*2 + 1*1 0100 = 0004 -> from 0*8 + 1*4 + 0*2 + 0*1 0101 = 0005 -> from 0*8 + 1*4 + 0*2 + 1*1 8 = 2^3, 4 = 2^2, 2=2^1 and 1 = 2^0 Then base2 = base10 .0000 = .0000 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .0001 = .0625 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .0010 = .1250 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .0011 = .1875 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .0100 = .2500 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .0101 = .3125 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .0110 = .3750 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .0111 = .4375 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1000 = .5000 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1001 = .5625 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1010 = .6250 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1011 = .6875 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1100 = .7500 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1101 = .8125 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1110 = .8700 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1111 = .9325 -> from 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 1 = 2^0, 0.5 = 2^-1, 0.25=2^-2 and 0.125 = 2^-3
正如你所看到的,4位浮点数只能表示0到0.9325之间的10进制数,差距为0.0625,这也意味着它不能表示0.1,0.2,0.3 ......。由于实际的标准使用更多的位以及使用数字移位技术。它确实可以表示比这个例子更多的数字,但限制仍然相同。所以当你除以某个值,结果没有落在其中之一... JVM将它移动到最接近的一个。希望这个解释。
4条答案
按热度按时间5lhxktic1#
这是有史以来最常被问到的问题之一,所以我在这里提出几点。
1.计算机只能表示有限位数,因此在存储数字和随后对数字进行除法运算时,必须进行四舍五入,这种四舍五入自然会产生误差,但如果你只想要精度为3位的数字,那么这些误差对你来说就不重要了。
1.舍入的行为有点不可预测,因为计算机是以二进制存储数字的,所以19.7是一个终止十进制数,同样的数字是一个二进制的循环十进制数--10011.1011001100110011001100110011......因此,您可以看到,在任意点舍入将产生无法从终止十进制表达式预测的行为。
oxf4rvwz2#
Mystical给出的链接是必读的,但是有点厚。尝试this site,获得一个更友好的入门版本。
tl;dr是指浮点运算总是要舍入的,而双精度,由于精度更高,舍入的方式和浮点数不同,有点像55舍入到最接近的10等于60,但是舍入到最接近的100等于100。
在这种情况下,你不能用浮点数或双精度数精确地表示十进制数0.197(或19.7),所以每一个都给出了它所能表示的最接近该值的数字,双精度数可以更接近一点,因为它有更高的精度。
e5nszbig3#
这不是除法的问题,问题是由于精度损失导致1.7f!= 1.7。我们可以看看值的位表示
输出
8nuwlpux4#
Java使用IEEE754浮点数来处理它的浮点数和双精度数。这个标准被设计来伊势不能被用来精确地表示基数为10的基数为2的数。参见这里http://en.wikipedia.org/wiki/IEEE_floating_point。
下面的例子并不完全是标准的,只是让你了解一下为什么基2浮点不适合其他基。
正如你所看到的,4位浮点数只能表示0到0.9325之间的10进制数,差距为0.0625,这也意味着它不能表示0.1,0.2,0.3 ......。
由于实际的标准使用更多的位以及使用数字移位技术。它确实可以表示比这个例子更多的数字,但限制仍然相同。所以当你除以某个值,结果没有落在其中之一... JVM将它移动到最接近的一个。
希望这个解释。