【问题标题】:Java multiply operation behaviorJava乘法运算行为
【发布时间】:2009-02-04 17:02:01
【问题描述】:

我写了一个将给定数字从天数转换为毫秒数的方法:

private long expireTimeInMilliseconds;
...
public void setExpireTimeInDays(int expireTimeInDays)
{
   expireTimeInMilliseconds = expireTimeInDays * 24 * 60 * 60 * 1000;
}

我很难弄清楚我做错了什么。现在我的问题: 这个错误这么明显吗?

修正方法:

private long expireTimeInMilliseconds;
...
public void setExpireTimeInDays(int expireTimeInDays)
{
   expireTimeInMilliseconds = ((long) expireTimeInDays) * 24 * 60 * 60 * 1000;
}

如果我在计算之前没有将整数转换为长整数,我会得到一个完全错误的结果。

【问题讨论】:

  • 你也可以在常量后面加上一个 L。
  • 人们为什么要关闭这个?似乎是一个可能对其他人有所帮助的合理问题。如果是完全重复的,请说出来。
  • 你也可以声明常量:24L * 60L...
  • 有趣的话题,但我认为这里的实际问题需要改变。 “错误是否明显”很容易受到影响。诸如“为什么 Java 不将其转换为 'long',即使我的变量是 'long' 类型?”之类的东西?似乎是我们在这里实际讨论的内容。
  • @Outlaw 你是对的:“为什么 Java 不将它转换为 'long'”更适合这里。

标签: java overflow integer-overflow


【解决方案1】:

很明显吗?我想这取决于您使用 Java 的时间以及您必须处理毫秒的次数。当然,最多 24 天左右应该没问题...

我认为最大的提示应该是System.currentTimeMillis() 返回一个long。这是一个很好的迹象,表明毫秒数可能会变大。您设置的变量类型也应该是一个很好的提示。

当然,您知道,如果您对整数进行算术运算,结果将是int,并在溢出时回绕。这是否足够明显可以辩论,但这将是一个毫无意义的讨论。在 C# 中,如果您打开溢出检查,您会很快发现错误 - 但没有多少开发人员这样做(确实,我不这样做,尽管我可能应该这样做)。

【讨论】:

  • 我期待很长时间,我习惯使用 System.currentTimeMillis()。因为那不是一个新代码,所以我不记得我写它时的想法(可能我期待编译器有一些魔力)......
  • ...我认为我的大错误,就像你说的那样,是用整数进行算术运算,而不注意整数溢出。基本概念忘记了(其实java没学过,C/C++学过)
【解决方案2】:

是的,如果你以前做过,那就很明显了。每当您看到一串数字相乘时,您应该自动开始考虑整数溢出错误。在这种情况下,如果 expireTimeInDays 大于 24,您将设置为溢出。从技术上讲,您应该考虑溢出错误任何时候处理整数时,但是像这样将一组它们相乘应该是一个很大的危险信号。

【讨论】:

    【解决方案3】:

    您的操作数变量和字面数字是 int 类型。 int 数据类型的最大值为 2^31 -1。因此,对于这么大的数字,int 的数据类型会溢出,导致看起来不正确的答案。

    在您的第一个示例中,int 仅提升为 long 分配给发生在 计算之后的变量。计算的结果是一个int。

    第二个示例,将第一个操作数强制转换为 long,导致计算提升为 long。在这种情况下,由于提升,计算的结果很长。 long 数据类型对于您的计算来说绰绰有余。

    【讨论】:

    • 看起来 Java 对于 int 溢出的行为与 C 相同。我想我希望编译器为我将它转换(int 到 long)。这是一种看起来很简单的代码(确实如此)......我只需要多加注意。
    【解决方案4】:

    您可能有兴趣知道这在 Joshua Bloch 和 Neal Gafter 的“Java Puzzlers”中有所介绍。


    (来源:javapuzzlers.com

    在那本书中,您会发现许多其他 Java 陷阱、陷阱和极端案例。

    我同意留下评论的starblue。将 L 附加到数字。

    【讨论】:

      【解决方案5】:

      不,这并不明显。

      但是相信我,经过几年的实践和修复这样的错误,你对整数溢出变得非常敏感,并且做正确的事情甚至不考虑它。

      这是发生在每个人身上的事情。绝对没有不良代码实践、无知等迹象。

      【讨论】:

      • 有趣的是,我已经编码了 20 多年,这是我第一次犯这个特定的错误。当我还是一名 C/C++ 开发人员时,我曾经关注我代码中的每一个小细节。我认为现在我期待编译器为我做一些“魔术”......
      【解决方案6】:

      只是为了补充其他答案,我发现过去定义常量 (public static final long) 很有帮助,例如 MILLISECS_DAYMILLISECS_HOUR。 更具可读性和实用性。

      【讨论】:

        【解决方案7】:

        另一种写法是

        public void setExpireTimeInDays(int expireTimeInDays)
        {
           expireTimeInMilliseconds = (long) expireTimeInDays * 24 * 60 * 60 * 1000;
        }
        

        public void setExpireTimeInDays(int expireTimeInDays)
        {
           expireTimeInMilliseconds = expireTimeInDays * 24L * 60 * 60 * 1000;
        }
        

        【讨论】:

        • 这种方式更好,我更喜欢避免不必要的强制转换。谢谢
        【解决方案8】:

        如果您在代码中使用 FindBugs,它会检测到这个确切的问题。 “ICAST:整数乘法转换为长整数的结果。” FindBugs 的例子正是你正在做的;以毫秒为单位计算天数。

        我第一次遇到这个问题时并不明显。

        【讨论】:

          【解决方案9】:

          有一些静态分析工具(findbugs)可以发现这些类型的错误。

          计算机上的数值数学可能很难。操作顺序会以您意想不到的方式影响精度和准确性。日期数学也可能非常棘手。通常使用 Date/Calendar 例程比尝试自己进行数学计算更好,但这些例程并不是 java 类库中设计最好的例程。

          【讨论】:

            【解决方案10】:

            我并不是要为我的错误辩护,但如果 java 编译器足够聪明,可以在计算之前将 int 提升为 long(一旦将计算分配给 long 类型的变量),那就太好了/p>

            顺便说一句,我曾经使用过 C/C++,如果是 C 程序,我也会遇到同样的问题,但几年前我对这种操作更加小心。

            下次我会多注意(或者改用python)... :D

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-05-19
              • 1970-01-01
              • 2012-01-22
              • 1970-01-01
              相关资源
              最近更新 更多