RE•DONE

Blog by Aliaksei Belski

My first acquaintance with weird number calculation behavior had happened back in 2012 when I realized that getting a sum of two fractions did not return an expected result. It was similar to

`const sum = 0.1 + 0.2; // 0.30000000000000004`

and returned an inaccurate result. This is how I met *IEEE 754* specification for the first time, and I got that not every decimal can be precisely represented (there is a good StackOverflow answer).

This is a pretty common error when developers start counting money and operate with currencies that have a decimal part (frankly, most of them). But there is a solution.

Strive to work with integers.

May sound naive, but it may save hours of debugging in a big application. Just compare two solutions: the first one calculates decimals as is…

```
const purchase1 = 3.57; // dollars
const purchase2 = 99.99; // dollars
const result = purchase1 + purchase2; // 103.55999999999999
```

…and second one has exact precision for UI.

```
const purchase1 = 357; // cents
const purchase2 = 9999; // cents
const precision = 2;
const result = (purchase1 + purchase2) / 10 ** precision; // 103.56
```

Thankfully, Javascript’s `number`

is big enough to store such values as purchases safely (`Number.MAX_SAFE_INTEGER`

equals to `9007199254740991`

).

As a second option, think of your rounding tactic to get precise and predictable results.

There are several more unusual values defined by the standard:

`0`

/`-0`

`Infinity`

/`-Infinity`

`NaN`

(Not a Number). Yes, this value is also a Number.

In reality, it’s hard to get a negative zero. In most calculations such as `3 - 3`

it’ll be a still regular `0`

. However, we can still get a negative one by dealing with `Infinity`

:

`const negativeZero = 0 / -Infinity; // -0`

But even in this case, you likely won’t be affected by negative zero, as comparison `+0 === -0`

still returns true. However, ES2015 introduced `Object.is()`

, which helps us to determine values like `-0`

:

`const isNegativeZero = Object.is(0, -0); // false`

Talking about `NaN`

. Despite the fact that it is considered as a Number, in JS there are plenty of ways to determine this exception, like:

```
console.log(NaN !== NaN); // true
console.log(isNaN(NaN)); // true
console.log(Number.isNaN(NaN)); // true
console.log(Object.is(NaN, NaN)); // true
```

All of them are good except (#2). I don’t recommend using `isNaN(NaN)`

because results of it may be unexpected (coercion inside makes it not precise and unreliable).

`console.log(isNaN("🥕")); // also true and makes sense, it's a carrot!`

In reality, it is rather simple to get an `Infinity`

and ruin your calculations. The most simple case is division by zero. Don’t think your `try/catch`

statement will help you, because it is not a case for *IEEE 754*. You’ll get a surprise instead:

```
const fancyCalculation = 1 / 0; // Infinity!
const anotherCalculation = 0 / 0; // Guess what? NaN!
```

Thankfully, together with ECMAScript 2015 we’ve got a good tool to exclude corner cases after a calculation, and it’s called `Number.isFinite()`

(like `isFinite`

but no coercion so more precise results).

```
console.log(Number.isFinite(NaN)); // false
console.log(Number.isFinite(Infinity)); // false
```

You should be safe enough now!

IEEE 754 specification may be misleading, and its predefined behavior casts a shadow on Javascript. But who said development is simple? Just handle corner cases, and write tests. Having one type for numbers (in opposite to such language as Java) is still good enough for a language, which was developed in ten days.