6

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

  1. Casting Example

    int days = (int) ((toDate.getTime() - fromDate.getTime()) / (1000 * 60 * 60 * 24));

  2. 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
Buddhika Ariyaratne
  • 2,339
  • 6
  • 51
  • 88
  • Just to be sure: you have a check in your code that ensures that "fromDate" is "smaller" than "toDate"? – GhostCat Apr 14 '15 at 15:05
  • It is not because of the Date calculation, it comes from the casting. I used System.out.println to check. – Buddhika Ariyaratne Apr 14 '15 at 15:06
  • 1
    The first example should be faster as there is no object creation (casting from a long to an int should be a single CPU cycle). In the second example, because of "autoboxing", you are basically creating a new object. This is the same as saying "new Long(((toDate.getTime() - fromDate.getTime()) / (1000 * 60 * 60 * 24))).intValue()" – BretC Apr 14 '15 at 15:11
  • You should use `TimeUnit` –  Apr 14 '15 at 15:16
  • The only difference is that the first one is (int) and the other one is (int)(Long). Should not change the outcome – Oskar Kjellin Apr 14 '15 at 15:24
  • 1
    @BuddhikaAriyaratne just saw your updated tests... interesting, I don't see any implementation of intValue in the javadocs to avoid overflow though, it might be an undefined issue where overflow may or may not happen? What version of Java by the way – matrixanomaly Apr 14 '15 at 16:01
  • Java version is 1.7.0_71 – Buddhika Ariyaratne Apr 14 '15 at 16:02
  • `int` Casting is different in your original Post, and the new Edit you added: Original: `int days = (int) ((toDate.getTime() - fromDate.getTime()) / (1000 * 60 * 60 * 24));` Edit: `System.out.println("((int) (nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24)) = " + ((int) (nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24)));` – Mecon Apr 14 '15 at 16:19
  • @BuddhikaAriyaratne still can't find any mention of it protecting from overflows, so i don't think it's a good idea to leave it unchecked either way. i'm updating my answer for a few more solutions i found – matrixanomaly Apr 14 '15 at 16:24
  • Thank you. I will code accordingly. Thank you. – Buddhika Ariyaratne Apr 14 '15 at 16:27
  • my answer was unaccepted... but it really was the correct answer. There is no difference in the two approaches. – Mecon Apr 16 '15 at 17:36
  • Sorry. It is the best answer. Accepted. – Buddhika Ariyaratne Apr 16 '15 at 19:22

4 Answers4

7

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);
Mecon
  • 977
  • 1
  • 6
  • 17
  • The results are different. I also used output to show the difference and add it as an edit. – Buddhika Ariyaratne Apr 14 '15 at 16:00
  • I just checked your Edit. The last two lines are different from your original post. The second-last line, you are `int` casting the "substraction", and then dividing it. The "substraction" result is a really large number, hence it causes overflow. But in the last line, you are applying `long.intValue` to the combination of substration and the division. The value of which is very small (27), and hence doesn't cause over flow. – Mecon Apr 14 '15 at 16:17
  • my point is that the two operations are referring to: `(int)` and `Long.intValue()` will always return you same result – Mecon Apr 14 '15 at 16:25
  • 1
    The reason you got different values in your last Edit, is because your `(int)` operation was getting applied to ONLY the substraction part. It should be applied to result of the substraction - WITH - division. – Mecon Apr 14 '15 at 16:26
  • 1
    Retry your Edit with the second last line as this: `System.out.println("((int) (nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24)) = " + ((int) ((nextDeliveryDate.getTime() - expectedDeliveryDate.getTime()) / (1000 * 60 * 60 * 24))));` – Mecon Apr 14 '15 at 16:27
  • You are correct. There is no difference in casting and intValue methods. I will update question. – Buddhika Ariyaratne Apr 14 '15 at 16:42
5

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.

Jaroslaw Pawlak
  • 5,538
  • 7
  • 30
  • 57
  • I edited the code to show details. The example gives a case where the int value expected is 27. There is no overflow in the final result. – Buddhika Ariyaratne Apr 14 '15 at 15:58
  • Thank you. I should have made sure that the number is small enough to cast to int without overflow. Then I may have to use few additional lines. But if I use the intValue, it seems that I do not have to worry on that. – Buddhika Ariyaratne Apr 14 '15 at 16:09
2

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.

libik
  • 22,239
  • 9
  • 44
  • 87
2

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.

Community
  • 1
  • 1
matrixanomaly
  • 6,627
  • 2
  • 35
  • 58
  • Thank you very much for all the explanations. They are very valuable for me to avoid errors in the code in future. But as Mecon and others pointed out, there is a difference in the way I used casting and intValue methods in my edited code. There is essentially no difference in casting and using intValue methods. – Buddhika Ariyaratne Apr 14 '15 at 16:47