I have a scheduled job running, where I want to calculate time till last 30 days. For that I am getting a warning that numeric overflow detected in expression. How can I safely give 30 days?
#Override
#Scheduled(cron = "0 5 5 * * ?")
public void deleteRepliesAutomatically() {
if(schedulerActive.equals("true")) {
Session session = this.sessionFactory.getCurrentSession();
long now = System.currentTimeMillis();
// Below line gives warning.
long nowMinus1Week = now - (1000 * 60 * 60 * 24 * 30);
Timestamp nowMinus1WeekAsTimeStamp = new Timestamp(nowMinus1Week);
Query query = session.createQuery("from Replies as cm where cm.sortTimeStamp <:limit");
query.setParameter("limit", nowMinus1WeekAsTimeStamp);
List<Replies> repliesList = query.list();
for (Replies replies : repliesList) {
session.delete(replies);
session.flush();
}
}
}
Thank you.
The problem is your expression (1000 * 60 * 60 * 24 * 30) (resulting in 2.592.000.000) is a pure int-computation that is overflowing (Integer.MAX_VALUE is 2.147.483.647).
Just make e.g. the first number a long by adding a L:
(1000L * 60 * 60 * 24 * 30)
This will make the whole computation using the long datatype that is large enough to hold the value.
The answer of piet.t is in correct to fix the issue at hand.
But it would be better maintanable to use TimeUnit enum to calculate from one unit to another.
For example to have milliseconds of one week:
long millis = TimeUnit.DAYS.toMillis(7);
You can also do a import static for DAYS.
Related
I encountered a troublesome issue and I can't really explain to myself why it is appearing.
Basically I want to add time to a timestamp (a simple long).
I understand it the following. If I add time to a timestamp I end in the future. If I subtract time to the timestamp I end in the past.
In my example it is the other way around. If I add something to my timestamp it is reduced and if I subtract something is added.
public class MyClass {
public static void main(String args[]) {
static final int MONTH_IN_SECONDS = 2629743;
final long current = System.currentTimeMillis();
System.out.println("Current: " + current);
final long future = System.currentTimeMillis() + (MONTH_IN_SECONDS * 1000 * 3);
System.out.println("Addition: " + future);
final long past = System.currentTimeMillis() - (MONTH_IN_SECONDS * 1000 * 3);
System.out.println("Subtraction: " + past);
}
}
Result (compare the first 5 chars):
Current: 1582275101365
Addition: 1581574395774 // smaller than current even though it should be greater
Subtraction: 1582975806958 // great than current even though it should be smaller
Why does this happend? Does the term (MONTH_IN_SECONDS * 1000 * 3) overflow because it is only an Integer and thus the calculation does not work (or ends in a negative value)?
If I change the term to (MONTH_IN_SECONDS * 1000L * 3) it seems to work correctly. Is it because the complete term is casted to a long?
The problem is here:
(MONTH_IN_SECONDS * 1000 * 3)
That's integer multiplication that's overflowing, and resulting in a negative number:
System.out.println((MONTH_IN_SECONDS * 1000 * 3));
That outputs -700705592. You'd have to declare MONTH_IN_SECONDS as long, or otherwise change the expression so that the result is long-typed.
Does the term (MONTH_IN_SECONDS * 1000 * 3) overflow because it is
only an Integer and thus the calculation does not work (or ends in a
negative value)?
Month in seconds? Google says 2,630,000. (Though I see you have 2629743.)
2,630,000 * 1000 * 3 = 7,890,000,000
Integer.MAX_VALUE = 2^31 = 2,147,483,648
So yeah, it's an integer overflow.
long m = 24 * 60 * 60 * 1000 * 1000;
The above code creates overflow and doesn't print the correct result.
long m2 = 24L * 60 * 60 * 1000 * 1000;
long m3 = 24 * 60 * 60 * 1000 * 1000L;
The above 2 lines print the correct result.
My questions are-
Does it matter to the compiler which I use, m2 or m3?
How does java starts multiplying? Left to right or right to left? Does 24*60 gets computed first or 1000*1000?
In this case -
long m = 24 * 60 * 60 * 1000 * 1000;
The right of the assignment is evaluated first. At right there is no long type data. All are int. So the JVM try to fit the result in an int then the overflow occurred.
And in the second case -
long m2 = 24L * 60 * 60 * 1000 * 1000;
long m3 = 24 * 60 * 60 * 1000 * 1000L;
Here one operand of the multiplication is long. So other are prompted to long automatically. The result is trying to fit to a long. Finally the assignment is done with m2 and m3.
And yes the associativity of multiplication from left to right - means the left operand is taken first. And Based on this fact I think in this scenario we should use -
long m2 = 24L * 60 * 60 * 1000 * 1000;
this statement, since at this statement the promotion to long taken places earlier which reduces the risk of overflow.
I would use the m2 line instead of the m3 line.
Java evaluates the multiplication operator * from left to right, so 24 * 60 is evaluated first.
It just so happens that 24 * 60 * 60 * 1000 (one 1000) doesn't overflow, so that by the time you multiply by 1000L (the second 1000), the product is promoted to long before multiplying, so that overflow doesn't take place.
But as you mentioned in your comments, more factors can cause overflow in the int data type before multiplying the last long number, yielding an incorrect answer. It's better to use a long literal for the first (left-most) number as in m2 to avoid overflow from the start. Alternatively, you can cast the first literal as a long, e.g. (long) 24 * 60 * ....
Since expressions are evaluated from left to right, I would prefer your first solution (m2 = ...).
Reasoning: Let's look at a slightly different example.
long g = Integer.MAX_VALUE * 2 * 2L;
This expression will evaluate to -4 since only the last multiplication casts the first expression to long (which is -2 at this point in time, because both operands are int). If you write
long g = Integer.MAX_VALUE * 2L * 2;
instead, g will hold the expected value of 8589934588 since the first multiplication yields a result of type long.
Multiplying works from left to right, and int * int produces int. So
24 * 60 * 60 * 1000 * 1000
is same as
(((24 * 60)* 60) * 1000) * 1000
which gives us
(((1440)* 60) * 1000) * 1000
(( 86400 ) * 1000) * 1000
( 86400000 ) * 1000
and finally because of integer overflow (since 86400000000 is too big for integer which max value is 2147483647) result will be
500654080
You can eliminate integer overflow by using long as one of arguments (int * long and long * int produces long).
In this case you can do it at start like you did in case of m2: 24L * 60 which will produce long 1440L which again will be multiplied by int 60 producing new long, and so on, producing only long values.
m3 case works here because you are multiplying 86400000 by 1000L which means that you are avoiding integer overflow since result will be long.
Let's multiply more numbers, this line will overflow even there is a 1000L:
long m3 = 24 * 60 * 60 * 1000 * 1000 * 1000 * 1000L;
While this will give correct result:
long m3 = 24L * 60 * 60 * 1000 * 1000 * 1000 * 1000;
So we are sure that java start multiplying from left to right and we have to start with Long from the left to prevent overflow.
This is because when we use long as one operand the other all int type operand get prompted to long.
The expression in java evaluated from left to right.
I have a long value that needed to be converted to an integer. When I use casting, time to time integer value gives a minus value, which is not expected. But when I use the intValue() method in Long, the expected results comes.
I want to know the difference of casting and using intValue() method
Casting Example
int days = (int) ((toDate.getTime() - fromDate.getTime()) / (1000 * 60 * 60 * 24));
intValue Example
int days = ((Long) ((toDate.getTime() - fromDate.getTime()) / (1000 * 60 * 60 * 24))).intValue();
Edit: More Elaborate Example to show minus values without overflow as suggested in comments. Before casting the result is 27. When casting, the value becomes -22. But if intValue method is used, the result is 27.
Code
System.out.println("nextDeliveryDate = " + nextDeliveryDate);
System.out.println("nextDeliveryDate.getTime() = " + nextDeliveryDate.getTime());
System.out.println("expectedDeliveryDate = " + expectedDeliveryDate);
System.out.println("expectedDeliveryDate.getTime() = " + expectedDeliveryDate.getTime());
System.out.println("nextDeliveryDate.getTime() - expectedDeliveryDate.getTime() = " + (nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()));
System.out.println("(nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24) = " + (nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24));
System.out.println("((int) (nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24)) = " + ((int) (nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24)));
System.out.println("((Long) ((nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24))).intValue() = " + ((Long) ((nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24))).intValue());
Results
Info: nextDeliveryDate = Thu May 14 00:00:00 IST 2015
Info: nextDeliveryDate.getTime() = 1431541800000
Info: expectedDeliveryDate = Fri Apr 17 00:00:00 IST 2015
Info: expectedDeliveryDate.getTime() = 1429209000000
Info: nextDeliveryDate.getTime() - expectedDeliveryDate.getTime() = 2332800000
Info: (nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24) = 27
Info: ((int) (nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24)) = -22
Info: ((Long) ((nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24))).intValue() = 27
I don't think your results should be different, because here is what the Long.intValue() method does:
public int intValue() {
return (int)value;
}
Something isn't right with your control flow between those two lines.
You can try this test below:
Calendar now = Calendar.getInstance();
Date fromDate = now.getTime();
now.add(Calendar.YEAR, 10);
Date toDate = now.getTime();
System.out.println("line 0: " + (toDate.getTime() - fromDate.getTime()));
System.out.println("line 1: " + (int)(toDate.getTime() - fromDate.getTime()));
System.out.println("line 2: " + new Long((toDate.getTime() - fromDate.getTime())).intValue());
int days = (int) ((toDate.getTime() - fromDate.getTime()) / (1000 * 60 * 60 * 24));
System.out.println("line 3: " + days);
days = ((Long) ((toDate.getTime() - fromDate.getTime()) / (1000 * 60 * 60 * 24))).intValue();
System.out.println("line 4: " + days);
You may be hitting int overflow. If the difference between toDate.getTime() and fromDate.getTime() is greater than 2.147 bilion (i.e. Integer.MAX_VALUE), it won't be possible to express it as an integer and it will just wrap and change to minimum value - e.g. Integer.MAX_VALUE + 100 will give you negative result.
You should do all the operations on longs first until you are sure that it is small enough to be cast to int without overflow. I think both your examples are correct, the first is definitely much clearer than the other.
Also, remember that expressions like 1000 * 60 * 60 * 24 are integers and they may overflow too. For example, the number of miliseconds in a month is 31 * 24 * 3600 * 1000, however written like this it returns -1616567296.
EDIT: Let me explain the output you have attached in your edit :
Info: nextDeliveryDate.getTime() - expectedDeliveryDate.getTime() = 2332800000
Info: (nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24) = 27
Info: ((int) (nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24)) = -22
Info: ((Long) ((nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24))).intValue() = 27
I will also refer to nextDeliveryDate.getTime() - expectedDeliveryDate.getTime() as x. So we can declare it as long x = 2332800000 (declaring int will give compilation error as it is too large to be hold in 4 bytes).
In the first line, you have x / something, something is an int and x is long, so something will be converted to long, the final result is long.
In the second line, you have ((int) x) / something, so long 2332800000 is cast to integer giving -1962167296 and then you have -1962167296 / something.
In your last line, you have (Long) (x / something) which is effectively the same case as first one.
There is no difference, this code
public static void main(String[] args) {
System.out.println((int) (21470000000l));
System.out.println((int) ((Long)21470000000l).intValue());
}
Has this output :
-4836480
-4836480
Therefore you have different values in your toDate and fromDate.
If the value exceed the MAX of integer, you never get the "expected" result, because it just cant fit in there.
Firstly, to explain a little about the difference between casting to int and IntValue() method in Java.
IntValue() is a method in the Integer class (and other wrapper classes).
What IntValue will do is get the integer and return it as an int type.
Note that int is an primitive datatype and Integer is a class that holds this datatype. It's like a wrapper, because there are data structures that cannot hold primitive datatypes. See this SO question for more info
Basically:
Converting an int to Integer is called boxing.
Converting an Integer to int is called unboxing
As noted in the SO question I linked.
However, as for your example, this is a little different because of the types involved. The difference is that you perform all operations as a Long datatype, which holds a lot more numbers than an int first, for accuracy, then only convert it to an int. So your IntValue() method is from the long datatype, which Java7 docs mention of it's use. Basically it just changes it from a long to an int, which still can cause overflows.
Note that Long is the wrapper class for the primitive datatype long (in which the primitive datatype does not have the IntValue() method)
So the difference in values you get may be because when you did the simple cast from Long -> int primitive datatypes, there wasn't an overflow.
I suggest you test out your code with really large numbers that don't change (unlike date,time which is different every time you fetch it)
Solving the overflow problem
You also might be interested in how to safely cast a long to an int in java as discussed in this SO question as an alternative solution for handling the overflow possibility.
Another way I would suggest is to first cut down the precision (hence you lose precision) into a safe enough value (and precise enough one) before casting it back to an int for use.
Or, use BigIntegers, which are immutable arbitrary-precision integers. see the the documentation on this class and this SO Q&A on how to use it. It is definitely going to add complexity to the code as they are immutable.
This other SO question has good solutions that give nice methods on detecting overflow in ints and longs which I also recommend as well.
I'm trying to do some basic calculation stuff in my android app to compare a Date.getTime() value with some calculated stuff.
The calculation I do during a database query is:
long minus = pauseDays * 24 * 60 * 60 * 1000;
So basically I calculate the millisecond-value of pauseDays. If pauseDays gets bigger (I'm talking about 90 days or so), something strange happens. The result of the calculation is a negative number.
The weird thing is, that the result should be 7776000000, so it should be way smaller than Long.MAX_VALUE. Could anybody explain to me why I get a negative number here?
The reason is probably because pauseDays is an int type, right? Then you are multiplying it by another bunch of ints, then converting it to long.
Consider this:
public class Main {
public static void main(String[] args) {
int pauseDays = 90;
long minus = pauseDays * 24 * 60 * 60 * 1000;
System.out.println(minus);
long pauseDaysL = 90L;
long minusL = pauseDaysL * 24L * 60L * 60L * 1000L;
System.out.println(minusL);
}
}
The output of this is:
-813934592
7776000000
Notice that the first long minus uses integers to generate its value. The second long minusL uses all long integer values.
I'm writing a program that asks the user for their birthdate and then calculates their age in years of different planets. I am not suppose to assume how the birthdate is to be entered except that there is one white space between each number.
The code I have right now does not meet these specifications right now and I'm not sure how to write it otherwise. I am also having problem calculating what my age would be today. When I enter my birthdate and print out age, it currently tells me that I'm 407 yet when I print out dateBirth and today, both of those dates are correct.
System.out.print("Please enter your birthdate (mm dd yyyy): ");
birthdate = scan.nextLine();
DateFormat df = new SimpleDateFormat("MM dd yyyy");
Date dateBirth = df.parse(birthdate);
Calendar calBirth = new GregorianCalendar();
calBirth.setTime(dateBirth);
Calendar calDay = new GregorianCalendar();
today = calDay.getTime();
age = (today.getTime() - dateBirth.getTime()) / (1000 * 60 * 60 * 24 * 365);
1000 * 60 * 60 * 24 * 365 is actually 31536000000 which is bigger than Integer.MAX_VALUE this causes an overflow. As an integer it would be evaluated to 1471228928 which leads to the wrong result.
The solution is append the letter L to one of your constants
long div = ( 1000 * 60 * 60 * 24 * 365L );
long age = ( today.getTime() - dateBirth.getTime() ) / div;
You should check if the expression 1000 * 60 * 60 * 24 * 365 evaluates to the result you are expecting and if not, find a way to get the expected result. Perhaps you should even consider that on earth, we have so called leap years and that you could tag your question as homework.
1000 * 60 * 60 * 24 * 365
Is an int, but its to long to hold it. Make one of these a long, like:
1000L * 60 * 60 * 24 * 365