A Few Weird Things about Java Math - dummies

# A Few Weird Things about Java Math

Believe it or not, computers — even the most powerful ones — have certain limitations when it comes to performing math calculations. These limitations are usually insignificant, but sometimes they sneak up and bite you. Here are the things you need to watch out for when doing math in Java.

## Integer overflow

The basic problem with integer types is that they have a fixed size. As a result, there is a limit to the size of the numbers that can be stored in variables of type `short`, `int`, or `long`. Although `long` variables can hold numbers that are huge, sooner or later you come across a number that’s too big to fit in even a `long` variable.

Okay, consider this (admittedly contrived) example:

`int a = 1000000000;`

`System.out.println(a);`

`a += 1000000000;`

`System.out.println(a);`

`a += 1000000000;`

`System.out.println(a);`

`a += 1000000000;`

`System.out.println(a);`

Here you expect the value of `a` to get bigger after each addition. But here’s the output that’s displayed:

`1000000000`

`2000000000`

`-1294967296`

`-294967296`

The first addition seems to work, but after that, the number becomes negative! That’s because the value has reached the size limit of the `int` data type. Unfortunately, Java doesn’t tell you that this error has happened. It simply crams the `int` variable as full of bits as it can, discards whatever bits don’t fit, and hopes that you don’t notice. Because of the way `int` stores negative values, large positive values suddenly become large negative values.

The moral of the story is that if you’re working with large integers, you should use `long` rather than `int`, because `long` can store much larger numbers than `int`. If your programs deal with numbers large enough to be a problem for `long`, consider using floating-point types instead. Floating-point types can handle even larger values than `long`, and they let you know when you exceed their capacity.

## Floating-point weirdness

Floating-point numbers have problems of their own. For starters, floating-point numbers are stored using the binary number system (base 2), but humans work with numbers in the decimal number system (base 10). Unfortunately, accurately converting numbers between these two systems is sometimes impossible. That’s because in any number base, certain fractions can’t be represented exactly.

One example: Base 10 has no way to exactly represent the fraction 1/3. You can approximate it as 0.3333333, but eventually you reach the limit of how many digits you can store, so you have to stop. In base 2, it happens that one of the fractions you can’t accurately represent is the decimal value 1/10. In other words, a `float` or `double` variable can’t accurately represent `0.1`.

Try running this code:

`float x = 0.1f;`

`NumberFormat nf = NumberFormat.getNumberInstance();`

`nf.setMinimumFractionDigits(10);`

`System.out.println(nf.format(x));`

The resulting output is this:

`0.1000000015`

Although `0.1000000015` is close to` 0.1`, it isn’t exact.

In most cases, Java’s floating-point math is close enough not to matter. The margin of error is extremely small. If you’re using Java to measure the size of your house, you’d need an electron microscope to notice the error. If you’re writing applications that deal with financial transactions, however, normal rounding can sometimes magnify the errors to make them significant. You may charge a penny too much or too little sales tax. And in extreme cases, your invoices may actually have obvious addition errors.

Integer types are stored in binary too, of course. But integers aren’t subject to the same errors that floating-point types are — because integers don’t represent fractions at all — so you don’t have to worry about this type of error for `integer` types.

## Division by zero

According to the basic rules of mathematics, you can’t divide a number by zero. The reason is simple: Division is the inverse of multiplication — which means that if `a * b = c`, it is also true that `a = c / b`. If you were to allow `b` to be zero, division would be meaningless, because any number times zero is zero. Therefore, both `a` and `c` would also have to be zero. In short, mathematicians solved this dilemma centuries ago by saying that division by zero is simply not allowed.

So what happens if you do attempt to divide a number by zero in a Java program? The answer depends on whether you’re dividing integers or floating-point numbers. If you’re dividing integers, the statement that attempts the division by zero chokes up what is called an exception, which is an impolite way of crashing the program.

There is a way to intercept this exception to allow your program to continue which you don’t find out here. In the meantime, any program you write that attempts an integer division by zero crashes.

If you try to divide a floating-point type by zero, the results are not so abrupt. Instead, Java assigns to the floating-point result one of the special values listed in the table below. The following paragraphs explain how these special values are determined:

• If you divide a number by zero, and the sign of both numbers is the same, the result is positive infinity.` 0.0` divided by `0.0` is positive infinity, as is `-34.0` divided by `-0.0`.
• If you divide a number by zero, and the signs of the numbers are different, the result is negative infinity. `-40.0` divided by `0.0` is negative infinity, as is `34.0 `divided by `0.0`.
• If you divide zero by zero, the result is not a number (NaN), regardless of the signs.
 Constant Meaning `POSITIVE_INFINITY` Positive infinity `NEGATIVE_INFINITY` Negative infinity `NaN` Not a number

Floating-point zeros can be positive or negative. Java considers positive and negative zeros to be equal numerically.

If you attempt to print a floating-point value that has one of these special values, Java converts the value to an appropriate string. Suppose that you execute the following statements:

`double x = Math.sqrt(-50); // Not a number`

`double y = x;`

`if (x == y)`

`System.out.println(“x equals y”);`

The resulting console output is

`Infinity`

If `i` were `-50.0`, the console would display `-Infinity`, and if` i` were zero, the console would display `NaN`.

The following paragraphs describe some final bits of weirdness:

• `NaN` is not equal to itself, which can have some strange consequences. For example:

`double x = Math.sqrt(-50); // Not a number`
`double y = x;`
`if (x == y)`
`System.out.println("x equals y");`

Just assume, for the sake of argument, that the `if` statement tests whether the variable `x` is equal to the variable `y`. Because this test immediately follows an assignment statement that assigns the value of` x` to `y`, you can safely assume that `x` equals `y`, right?

Wrong. Because `x` is `NaN`, `y` also is `NaN`. `NaN`is never considered to be equal to any other value, including another` NaN`. Thus, the comparison in the `if` statement fails.

• Another strange consequence: You can’t assume that a number minus itself is always zero. Consider this statement:

`double z = x - x; // not necessarily zero`

Shouldn’t this statement always set `z` to zero? Not if `x` is `NaN`. In that case, not a number minus not a number is still not a number.

• One more weirdness: Any mathematical operation involving infinity results in either another infinity or `NaN`. Infinity + 5, for example, still equals infinity, so Buzz Lightyear’s call “To infinity and beyond!” just isn’t going to happen. But infinity minus infinity gives you … `NaN`.