java中的除法

bsxbgnwa  于 2023-01-04  发布在  Java
关注(0)|答案(4)|浏览(168)

我在Java中有一个简单的划分:

float f = 19.7f/100;
System.out.println(f); // 0.19700001

double d = 19.7/100;
System.out.println(d); // 0.19699999999999998

为什么会发生这种情况?

5lhxktic

5lhxktic1#

这是有史以来最常被问到的问题之一,所以我在这里提出几点。
1.计算机只能表示有限位数,因此在存储数字和随后对数字进行除法运算时,必须进行四舍五入,这种四舍五入自然会产生误差,但如果你只想要精度为3位的数字,那么这些误差对你来说就不重要了。
1.舍入的行为有点不可预测,因为计算机是以二进制存储数字的,所以19.7是一个终止十进制数,同样的数字是一个二进制的循环十进制数--10011.1011001100110011001100110011......因此,您可以看到,在任意点舍入将产生无法从终止十进制表达式预测的行为。

oxf4rvwz

oxf4rvwz2#

Mystical给出的链接是必读的,但是有点厚。尝试this site,获得一个更友好的入门版本。
tl;dr是指浮点运算总是要舍入的,而双精度,由于精度更高,舍入的方式和浮点数不同,有点像55舍入到最接近的10等于60,但是舍入到最接近的100等于100。
在这种情况下,你不能用浮点数或双精度数精确地表示十进制数0.197(或19.7),所以每一个都给出了它所能表示的最接近该值的数字,双精度数可以更接近一点,因为它有更高的精度。

e5nszbig

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
8nuwlpux

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将它移动到最接近的一个。
希望这个解释。

相关问题