I am trying to write a code to determine when the number of milliseconds since the beginning of 1970 will exceed the capacity of a long. The following code appears to do the job:
public class Y2K { public static void main(String[] args) { int year = 1970; long cumSeconds = 0; while (cumSeconds < Long.MAX_VALUE) { // 31557600000 is the number of milliseconds in a year cumSeconds += 3.15576E+10; year++; } System.out.println(year); } }
This code executes within seconds and prints 292272992. If instead of using scientific notation I write cumSeconds as 31558000000L
, the program seems to take “forever” to run (I just hit pause after 10 mins or so). Also notice that writing cumSeconds in scientific notation does not require specifying that the number is a long
with L or l at the end.
The primary reason for converting numbers into scientific notation is to make calculations with unusually large or small numbers less cumbersome. Because zeros are no longer used to set the decimal point, all of the digits in a number in scientific notation are significant, as shown by the following examples.
Scientific notation is like shorthand for writing very large or very small numbers. Instead of writing the number in decimal form, the number is shortened to a number multiplied by a power of ten.
To write a large number in scientific notation, move the decimal point to the left to obtain a number between and . Since moving the decimal point changes the value, you have to multiply the decimal by a power of so that the expression has the same value. Let's look at an example.
Scientific notation is a way of writing very large or very small numbers. A number is written in scientific notation when a number between 1 and 10 is multiplied by a power of 10. For example, 650,000,000 can be written in scientific notation as 6.5 ✕ 10^8. Created by Sal Khan and CK-12 Foundation.
The reason it makes a difference is because the scientific notation number 3.1558E+10
is a double
literal, whereas the literal 31558000000L
is of course a long
literal.
This makes all the difference in the +=
operator.
A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T) ((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.
Basically, long += long yields a long, but long += double also yields a long.
When adding a double
, the initial value of cumSeconds
is widened to a double
and then the addition occurs. The result undergoes a narrowing primitive conversion back to long
.
A narrowing conversion of a floating-point number to an integral type T takes two steps:
- In the first step, the floating-point number is converted either to a long, if T is long
(snip)
Otherwise, one of the following two cases must be true:
The value must be too small (a negative value of large magnitude or negative infinity), and the result of the first step is the smallest representable value of type int or long.
The value must be too large (a positive value of large magnitude or positive infinity), and the result of the first step is the largest representable value of type int or long.
(bold emphasis mine)
The result eventually is too big to be represented in a long
, so the result is narrowed to Long.MAX_VALUE
, and the while
loop ends.
However, when you use a long
literal, you are continuously adding an even value to an even value, which will eventually overflow. This does not set the value to Long.MAX_VALUE
, which is odd, so the loop is infinite.
But instead of relying on an addition eventually yielding Long.MAX_VALUE
, with Java 1.8+ you can explicitly test for overflow with Math.addExact
.
Returns the sum of its arguments, throwing an exception if the result overflows a long.
Throws:
ArithmeticException
- if the result overflows a long
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With