Why do these two multiplication operations give different results?
long oneYearWithL = 1000*60*60*24*365L;
long oneYearWithoutL = 1000*60*60*24*365;
Your first value is actually a long (Since 365L
is a long
, and 1000*60*60*24
is an integer
, so the result of multiplying
a long
value with an integer
value is a long
value.
But 2nd value is an integer (Since you are mulitplying an integer
value with an integer
value only. So the result will be a 32-bit
integer. Now the result obtained for that multiplication
is outside the actual range of integer. So, before getting assigned to the variable, it is truncated to fit into valid integer range.
Take a look at the following print statement: -
System.out.println(1000*60*60*24*365L);
System.out.println(1000*60*60*24*365);
System.out.println(Integer.MAX_VALUE);
When you run the above code: -
Output: -
31536000000
1471228928
2147483647
So, you can see the difference..
011101010111101100010010110000000000 -- Binary equivalent of 1000*60*60*24*365L
01111111111111111111111111111111 -- Binary equivalent of Integer.MAX_VALUE
So, if you don't add that L
at the end of your number, the 4 most significant bit is removed from the first binary string..
So, the string becomes..
(0111)01010111101100010010110000000000 -- Remove the most significant bits..
01010111101100010010110000000000 -- Binary equivalent of 1471228928
(which you get as output)
UPDATE: -
From the above explanation, you can also understand that, even in the first assignment, if the result of your multiplication
of integers
before multiplying it with 365L
goes out of range, then again it will be truncated to fit in integer range, or converted to 2's complement representation
if required, and then only it will be multiplied with the long value - 365L
.
For e.g: -
long thirtyYearWithL = 1000*60*60*24*30*365L;
In the above example, consider the first part - 1000*60*60*24*30
. The result of this multiplication is: - 2592000000
. Now lets' see how it is represented in binary equivalent
: -
2592000000 = 10011010011111101100100000000000 -- MSB is `1`, a negative value
01100101100000010011100000000001 -- 2's complement representation
Decimal representation of the 2's complement
representation is 1702967297
. So, 2592000000
is converted to -1702967297
, before getting multiplied to 365L
. Now since, this value fits in the integer range
which is : - [-2147483648 to 2147483647]
, so it will not be truncated further.
So, the actual result will be: -
long thirtyYearWithL = 1000*60*60*24*30*365L;
= 2592000000 * 365L;
= -1702967297 * 365L = -621583063040
So, all these stuffs just considers the actual type
of final result on applying the arithmetic operation. And this check is performed on each temporary result of operations moving from left to right
(considering operators with left-to-right
associativity). If any temporary result is found to be out of range, then that is converted accordingly to fit in the required range, before moving forward with next operation.
UPDATE 2: -
So, instead of: -
long thirtyYearWithL = 1000*60*60*24*30*365L;
if you move your 365L
at the start, then you will get the correct result: -
long thirtyYearWithL = 365L*1000*60*60*24*30; // will give you correct result
Because, now your temporary
result will be of type long
, and is capable of holding that value.
Without the L
your computation is carried out as a 32-bit value. If you express the values as hexadecimal, the smaller value is just the lower 4 bytes of the larger value.
Java defaults to the 32 bit integer type. The L
, for Long, is 64 bits. By putting L
after 365
, you are telling the compiler to treat 365 as a long
value. When 32 and 64 bit values are multiplied, the compiler upcasts the 32 bit value to 64 bit, so that your intermediate results in evaluating your expression retain the full 64 bit range.
See Primitive Types and Values in the Java Language Specification.
The behavior of multiplication is explicitly defined at https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.17.1, as follows:
15.17.1 Multiplication Operator *
The binary
*
operator performs multiplication, producing the product of its operands. Multiplication is a commutative operation if the operand expressions have no side effects. While integer multiplication is associative when the operands are all of the same type, floating-point multiplication is not associative. If an integer multiplication overflows, then the result is the low-order bits of the mathematical product as represented in some sufficiently large two's-complement format. As a result, if overflow occurs, then the sign of the result may not be the same as the sign of the mathematical product of the two operand values.The result of a floating-point multiplication is governed by the rules of IEEE 754 arithmetic:
- If either operand is
NaN
, the result isNaN
.- If the result is not
NaN
, the sign of the result is positive if both operands have the same sign, and negative if the operands have different signs.- Multiplication of an infinity by a zero results in NaN.
- Multiplication of an infinity by a finite value results in a signed infinity. The sign is determined by the rule stated above.
- In the remaining cases, where neither an infinity nor NaN is involved, the exact mathematical product is computed. A floating-point value set is then chosen:
- If the multiplication expression is FP-strict (§15.4):
- If the type of the multiplication expression is
float
, then the float value set must be chosen.- If the type of the multiplication expression is
double
, then the double value set must be chosen.- If the multiplication expression is not FP-strict:
- If the type of the multiplication expression is
float
, then either the float value set or the float-extended-exponent value set may be chosen, at the whim of the implementation.- If the type of the multiplication expression is
double
, then either the double value set or the double-extended-exponent value set may be chosen, at the whim of the implementation.Next, a value must be chosen from the chosen value set to represent the product.
If the magnitude of the product is too large to represent, we say the operation overflows; the result is then an infinity of appropriate sign.
Otherwise, the product is rounded to the nearest value in the chosen value set using IEEE 754 round-to-nearest mode. The Java programming language requires support of gradual underflow as defined by IEEE 754 (§4.2.4).
Despite the fact that overflow, underflow, or loss of information may occur, evaluation of a multiplication operator
*
never throws a run-time exception.