【问题标题】:Loss of precision when casting Math.pow()铸造 Math.pow() 时精度损失
【发布时间】:2018-05-21 16:36:47
【问题描述】:

我试图计算 int 的最大值,但是我无法弄清楚为什么转换为 int 的计算值低于转换为 long 时的值。我在计算器上提出(2^31) - 1,值为

2_147_483_647

当我在 Java 中上升 2^31 并减去 1 时,我会期望相同,它应该计算 int 2147483647 的最大数,但它没有。

System.out.println(((long) Math.pow(2, 31)) - 1); 
System.out.println((int) Math.pow(2, 31) - 1); 

第一次计算得到:2_147_483_647

第二次计算得到:2_147_483_646

【问题讨论】:

  • Java 在转换 double -> int 时不会溢出,这就是数学不检查的原因

标签: java casting int


【解决方案1】:

强制转换优先于减法。你应该先做减法:

 System.out.println((int) (Math.pow(2, 31) - 1)); 
  • Math.pow(2, 31)2_147_483_648,类型为 double(因为 Math.pow() 返回双精度数),这个数字不适合 int。

  • 当您将双精度 2_147_483_648 强制转换为 int 时,它会被截断为 int 可以容纳的最大值(请参阅 How does double to int cast work in Java)。

  • 强制转换将双精度 2_147_483_648 截断为整数 2_147_483_647

  • 然后您的代码减去 1,并以 2_147_483_646 结束。

【讨论】:

    【解决方案2】:

    第一个例子

    让我们分析一下你做了什么。第一个例子是

    ((long) Math.pow(2, 31)) - 1;
    

    这意味着您首先计算 Math.pow(2, 31) 从而产生

    2.147_483_648 E9
    

    然后你投到long。注意long 有足够的地方存放这个值,所以它表示为

    2_147_483_648
    

    然后你减去1得到

    2_147_483_647
    

    第二个例子

    现在让我们看看第二个例子做了什么。你写的

    (int) Math.pow(2, 31) - 1
    

    这被解释为

    ((int) Math.pow(2, 31)) - 1
    

    所以和以前一样,但转换为int 而不是long。现在请注意,int 确实 没有足够的位置 存放该值。最大可以表示的int

    2_147_483_647 // MAX_INTEGER
    

    因此您也将在此处获得该值。之后你减去1 并得到

    2_147_483_646
    

    更正

    我想你是这么想的

    (int) Math.pow(2, 31) - 1
    

    评估为

    (int) (Math.pow(2, 31) - 1)
    

    但事实并非如此。如果你明确表示你确实会得到 ​​p>

    2_147_483_647
    

    正如预期的那样。


    运算符优先级

    您可以在 Java 语言规范 中阅读相关内容,请参阅 JLS§15.15 Unary Operators。强制转换优先于运算符,如减法。

    这是因为 castingunary operator 而减法是 binary。一元优先于二元。因此official documentation 给出了下表作为概述:

    +----------------------------------------------------------------+
    | Operators             | Precedence (top is high, bottom low)   |
    |-----------------------|----------------------------------------|
    | postfix               | expr++ expr--                          |
    | unary                 | ++expr --expr +expr -expr ~ !          |
    | multiplicative        | * / %                                  |
    | additive              | + -                                    |
    | shift                 | << >> >>>                              |
    | relational            | < > <= >= instanceof                   |
    | equality              | == !=                                  |
    | bitwise AND           | &                                      |
    | bitwise exclusive OR  | ^                                      |
    | bitwise inclusive OR  | |                                      |
    | logical AND           | &&                                     |
    | logical OR            | ||                                     |
    | ternary               | ? :                                    |
    | assignment            | = += -= *= /= %= &= ^= |= <<= >>= >>>= |
    +----------------------------------------------------------------+
    

    铸造当然是一元类别的一部分。

    【讨论】:

      【解决方案3】:

      对整个 Math.pow(2, 31) - 1) 进行类型转换。像这样

      System.out.println((int) (Math.pow(2, 31) - 1));
      

      【讨论】:

        【解决方案4】:

        你把括号放错地方了。应该是:

        System.out.println((int) (Math.pow(2, 31) - 1));
        

        【讨论】:

          【解决方案5】:

          (int) Math.pow(2, 31) - 1 这样编写代码会使 Java 将 Math.pow(2, 31) 转换为 int。由于这超过了 int 的最大值,因此结果为2_147_483_647。由于您之后减去 1,因此您得到的值低于实际解决方案。 您应该将整个计算转换为 int 而不仅仅是 Math.pow(2, 31) 所以它看起来像这样: (int) (Math.pow(2, 31) - 1)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-12-19
            • 2014-07-13
            • 1970-01-01
            • 1970-01-01
            • 2010-10-16
            • 2020-07-28
            • 2015-04-26
            • 1970-01-01
            相关资源
            最近更新 更多