【问题标题】:division in java [duplicate]java中的除法[重复]
【发布时间】:2013-02-16 18:46:34
【问题描述】:

我在 Java 中有一个简单的除法:

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

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

为什么会这样?

【问题讨论】:

标签: java floating-point


【解决方案1】:

Java 使用 IEEE754 浮点数来处理它的浮点数和双精度数。本标准设计为无法准确表示以 10 为底的以 2 为底的数字。见这里http://en.wikipedia.org/wiki/IEEE_floating_point

以下内容并不完全是标准,而是一个示例,只是为了让您了解为什么以 2 为基数的浮点数不适合其他基数。

基数 2 = 基数 10 0001 = 0001 -> 从 0*8 + 0*4 + 0*2 + 1*1 0010 = 0002 -> 从 0*8 + 0*4 + 1*2 + 0*1 0011 = 0003 -> 从 0*8 + 0*4 + 1*2 + 1*1 0100 = 0004 -> 从 0*8 + 1*4 + 0*2 + 0*1 0101 = 0005 -> 从 0*8 + 1*4 + 0*2 + 1*1 8 = 2^3, 4 = 2^2, 2=2^1 和 1 = 2^0 然后 基数 2 = 基数 10 .0000 = .0000 -> 从 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .0001 = .0625 -> 从 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .0010 = .1250 -> 从 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .0011 = .1875 -> 从 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .0100 = .2500 -> 从 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .0101 = .3125 -> 从 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .0110 = .3750 -> 从 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .0111 = .4375 -> 从 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1000 = .5000 -> 从 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1001 = .5625 -> 从 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1010 = .6250 -> 从 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1011 = .6875 -> 从 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1100 = .7500 -> 从 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1101 = .8125 -> 从 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1110 = .8700 -> 从 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 .1111 = .9325 -> 从 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 和 0.125 = 2^-3

如您所见。 4 位浮点数只能表示从 0 到 0.9325 的以 10 为基数的数字,间隙为 0.0625。这也意味着它不能做 0.1, 0.2, 0.3 ....

由于实际标准使用更多位以及使用数字移位技术。它实际上可以表示比这个例子更多的数字,但限制仍然相同。因此,当您划分某个值并且结果不属于其中之一时...JVM会将其移至最接近的值。

希望这能解释一下。

【讨论】:

    【解决方案2】:

    不是因为除法,问题是由于精度损失导致 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
    

    【讨论】:

      【解决方案3】:

      Mystical 给出的链接是必读的,但有点厚。尝试this site 以获得更适合初学者的版本。

      tl;dr 是浮点运算总是要进行舍入,并且由于具有更高的精度,双精度数的舍入方式与浮点数不同。这有点像 55 四舍五入到最接近的 10 会是 60,但四舍五入到最接近的 100 会是 100。

      在这种情况下,您不能将十进制数 0.197(或 19.7,就此而言)精确地表示为浮点数或双精度数,因此每个都为您提供了它可以的数字表示最接近那个值。双精度可以更接近一点,因为它具有更高的精度。

      【讨论】:

        【解决方案4】:

        这是有史以来最常被问到的问题之一,所以我将在这里提出几点。

        1. 计算机只能表示有限数量的数字,因此在存储数字和稍后除法时必须进行四舍五入。这种四舍五入自然会产生错误,但如果您只想要 3 位精度,那么它们对您的情况无关紧要。

        2. 四舍五入的行为有点不可预测,因为计算机以二进制形式存储数字。因此,虽然 19.7 是终止十进制,但相同的数字是二进制重复十进制 - 10011.10110011001100110011001100110011... ... 所以您可以看到在任意点四舍五入会产生无法从终止十进制表达式中预测的行为。

        【讨论】:

          猜你喜欢
          • 2023-04-02
          • 2014-01-27
          • 2021-04-17
          • 2018-03-13
          • 2013-12-24
          • 2014-04-23
          • 1970-01-01
          相关资源
          最近更新 更多