Java 乘法运算行为
我编写了一个方法将给定的数字从天转换为毫秒:
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;
}
如果我在计算之前不将整数转换为long,我会得到完全错误的结果。
I wrote a method to convert a given number from days to milliseconds:
private long expireTimeInMilliseconds;
...
public void setExpireTimeInDays(int expireTimeInDays)
{
expireTimeInMilliseconds = expireTimeInDays * 24 * 60 * 60 * 1000;
}
I had a hard time to figure out what I did wrong. Now my question:
Is that error so obvious ?
The corrected method:
private long expireTimeInMilliseconds;
...
public void setExpireTimeInDays(int expireTimeInDays)
{
expireTimeInMilliseconds = ((long) expireTimeInDays) * 24 * 60 * 60 * 1000;
}
If I don't convert the integer to long before calculating, I get a complete wrong result.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
很明显吗? 我想这取决于您使用 Java 的时间以及您处理毫秒的次数。 当然,最多24天左右应该没问题……
我想最大的提示应该是
System.currentTimeMillis()
返回一个long
。 这是一个很好的迹象,表明毫秒数可能会变大。 您设置的变量的类型也应该是一个很好的提示。当然,您还知道,如果您对整数进行算术运算,结果将是
int
,并在溢出时进行回绕。 这是否足够明显还可以争论,但这将是一个毫无意义的讨论。 在 C# 中,如果您打开溢出检查,您很快就会发现错误 - 但没有多少开发人员这样做(事实上,我不这样做,尽管我可能应该这样做)。Is it obvious? I guess it depends on how long you've been using Java and how many times you've had to deal with milliseconds. Of course, it should be okay for up to about 24 days...
I think the biggest hint should be that
System.currentTimeMillis()
returns along
. That's a good indication that a number of milliseconds can get big. The type of the variable you're setting should be a good hint too.Of course, you've also got to know that if you do arithmetic operations with ints, the result will be
int
with wrap-around on overflow. Whether that's sufficiently obvious or not could be debated, but it would be a pretty pointless discussion. In C# if you turned overflow checking on, you'd have found the bug pretty quickly - but then not many developers do that (indeed, I don't although I probably should).是的,如果您以前做过的话,那就很明显了。 每当您看到一串数字相乘时,您应该自动开始考虑整数溢出错误。 在这种情况下,如果
expireTimeInDays
大于 24,则设置为溢出。从技术上讲,任何时候使用整数都应该考虑溢出错误,但乘以像这样的一群人应该是一个非常大的危险信号。Yes, it's pretty obvious if you've done it before. Any time you see a string of numbers multiplied out you should automatically start thinking about integer overflow errors. In this case you're set to overflow if
expireTimeInDays
is more than 24. Technically you should be thinking about overflow errors any time you're working with integers, but multiplying a group of them like this should be a very big red flag.您的操作数变量和文字数字的类型为 int。 int 数据类型的最大值为 2^31 -1。 因此,对于如此大的数字,int 数据类型会溢出,从而导致看似不正确的答案。
在第一个示例中,int 仅在计算之后发生的变量赋值时提升为 long。 计算结果是一个int。
第二个示例将第一个操作数转换为 long,导致计算提升为 long。 在这种情况下,由于提升,计算结果很长。 long 数据类型对于您的计算来说足够大了。
Your operand variable and the literal numbers are of type int. The int data type has a maximum value of 2^31 -1. Therefore with such large numbers, the data type of int overflows leading to a seeming incorrect answer.
In your first example, the int is only promoted to a long on assignment to the variable which occurs after the calculation. The result of the calculation is an int.
The second example, casts the first operand to a long, causing the promotion of the calculation to a long. In this case, the result of the calculation is a long, due to promotion. The long data type is more than large enough for your calculation.
您可能有兴趣知道 Joshua Bloch 和 Neal Gafter 所著的《Java Puzzlers》对此进行了介绍。
(来源:javapuzzlers.com)
您将在那本书中发现许多其他 Java 陷阱、陷阱和极端情况。
我同意星蓝留下的评论。 在数字后添加一个 L。
You may be interested to know that this is covered in "Java Puzzlers" by Joshua Bloch and Neal Gafter.
(source: javapuzzlers.com)
You will find many other Java pitfalls, traps and corner cases in that book.
I agree with the starblue who left a comment. Append an L to the number.
不,这并不明显。
但是相信我,经过多年的实践和修复这样的错误,您会对整数溢出变得非常敏感,并且甚至不假思索地做正确的事情。
这是每个人都发生过的事情。 绝对没有不良代码实践、无知等迹象。
No, it's not obvious.
But trust me, after some more years of practice and fixing bugs like this you become very sensible about integer overflows and just do the right thing without even thinking about it.
It's something that happend to everyone. Definately no sign of bad code practice, ignorance or so.
只是为了添加其他答案,我发现过去定义常量(
public static final long
)很有帮助,例如MILLISECS_DAY
或MILLISECS_HOUR
>。更具可读性和实用性。
Just to add to the other answers, I have found it helpful in the past to define constants (
public static final long
) such asMILLISECS_DAY
orMILLISECS_HOUR
.Much more readable and useful.
另一种写法是
或
Another way to write this is
or
如果您在代码上使用 FindBugs,它将检测到这个确切的问题。 “ICAST:整数乘法的结果转换为长整型。” FindBugs 的示例正是您正在做的; 以毫秒为单位计算天数。
当我第一次遇到这个问题时,我并不明显。
If you use FindBugs on your code it will detect this exact problem. "ICAST: Result of integer multiplication cast to long." FindBugs' example is exactly what you are doing; calculating days in milliseconds.
This problem was not obvious to me the first time I ran into it.
有一些静态分析工具(findbugs)可以找到这些类型的错误。
计算机上的数值数学可能很困难。 操作顺序问题可能会以您意想不到的方式影响精度和准确度。 日期数学也可能非常棘手。 通常,使用日期/日历例程比尝试自己进行数学计算更好,但这些例程并不是 java 类库中设计最佳的例程。
There are some static analysis tool (findbugs) that will find these type of errors.
Numerical math on computers can be hard. Order of operation matters can affect precision and accuracy in ways that you don't expect. Date math can also be surprisingly tricky. Often it is better to use the Date/Calendar routines rather than trying to do the math yourself but those routines are not the best designed ones in the java class library.
我并不是想证明我的错误是正确的,但是如果java编译器足够聪明,能够在计算之前将int提升为long(一旦计算被分配给long类型的变量),那就太好
了我曾经使用C/C++,如果是C程序,我也会遇到同样的问题,但几年前我对这种操作更加小心。
下次我会多加注意(或切换到 python)...:D
I'm not trying to justify my mistake, but it would be great if the java compiler was smart enough to promote the int to a long before the calculation (once the calculation is being assigned to a variable of type long)
By the way, I used to work with C/C++ and if it was a C program, I'd had the same problem, but some years ago I'd been more careful with this kind of operation.
I'll pay more attention next time (or switch to python)... :D