JavaScript is sometimes accused of being bad with numbers. A common example is > 0.1 + 0.2 which evaluates to 0.30000000000000004. The result stems from how JavaScript deals with floating-point arithmetics, but people making the accusation often fails to mention that languages such as Java, Python, and Rust also evaluates this expression it to 0.30000000000000004, following the same standard for floating-point arithmetics (IEEE 754). In fact, when experimenting with various languages on repl.it only C/C++ produced correct results (but would fail with other floats).
Another point about JavaScript and numbers often raised is the size of large numbers. Out of the box, JavaScript cannot deal with numbers greater than, or smaller than, negative infinity (Number.NEGATIVE_INFINITY) and positive infinity (Number.POSITIVE_INFINITY). The max/min value of JavaScript can be obtained with Number.MIN_VALUE and Number.MAX_VALUE and are not very high (nor, respectively, very low). Therefore numbers in JavaScript can become a problem. Take this simplistic function producing factorials,
function fact(n) {
if (n === 0) return 0;
return Array
.from({ length: n }, (_, i) => i + 1)
.reduce( (acc, v) => acc * v, 1);
}
It’s not the best of implementations. The main problem though, is that it cannot handle the result of for instance fact(200), evaluated to Infinity.
This, on the other hand, is easily fixed using BigInt,
function fact2(n) {
if (n === 0) return 0;
return Array
.from({ length: n }, (_, i) => i + 1)
.reduce( (acc, v) => acc * BigInt(v), BigInt(1));
}
Using fact2 we can do for instance fact(20000) and still obtain the result.
That’s good. But BigInt comes with limitations. You can perform common arithmetic operations, but cannot use the methods of Math and others.
Solving the issue with floating-point arithmetics is more complicated. One solution would be to do use parseFloat: parseFloat((0.1 + 0.2).toFixed(2)) but this solution produces strange results.
I have come across functions similar to this one suggested as a solution,
function floatArithmetic(n) {
return parseFloat(n.toFixed(2), 10);
}
If one used it to add 0.1 with 0.2 we would obtain 0.3: floatArithmetic(0.1 + 0.2) . But since we fixate the precision beforehand, we get an errorneous result if we had inputted numbers with more than two decimals, floatArithmetic(0.001 + 0.002) (= 0.00).
The main obstacle here is that if we pass 0.1 + 0.2 as an argument the expression will be evaluated eagerly (also called strict or greedy evaluation). A simple, possible solution would provide the operation as a string and use eval (using eval is considered bad practice ).
const isFloat = (x) => x.includes(".");
function longestFloat(acc, v) {
const decLength = v.split(".")[1].length;
return acc > decLength ? acc : decLength;
}
function floatArithmetic(str) {
const regex = /[+-]?\d+(\.\d+)?/g;
const ns = str.match(regex).filter(isFloat);
if (ns.length) {
const mostDec = ns.reduce(longestFloat, 0);
return parseFloat(eval(str).toFixed(mostDec), 10);
}
return eval(str);
}
This would solve some problems: 0.001 + 002 = 0.003. On the other hand, only on the assumption that one could provide operations represented as a string - a far-fetched assumption. Another possibility would be using more advanced solutions from libraries, but all come - as far as I know - with limitations.