Reputation: 1500
I am trying to add two decimal numbers (arguments could be numbers or strings that are numbers before they are parsed) and compare the outcome with the resultInput
. The problem is that the floating-point numbers cannot be represented accurately enough by the system. For example, 0.1 + 0.2 = 0.30000000000000004
. So, I am trying to use toFixed()
method to format a number using fixed-point notation. I am getting false
when I run the code. Not sure where I am getting it wrong. Let me know if you have any ideas.
function calc(firstNumber, secondNumber, operation, resultInput) {
let a = parseFloat(firstNumber); //Number()
let b = parseFloat(secondNumber); //Number()
let c;
let d = parseFloat(resultInput);
console.log(JSON.stringify(`value of d : ${d}`)); //"value of d : NaN"
switch (operation) {
case '+':
c = a + b;
break;
case '-':
c = a - b;
break;
case '*':
c = a * b;
break;
case '/':
if (b === 0 && 1 / b === -Infinity) {
r = Infinity;
} else {
r = a / b;
}
break;
default:
console.log(`Sorry, wrong operator: ${operation}.`);
}
console.log(JSON.stringify(`value of c: ${c}`)); // "value of c: 0.30000000000000004"
let f = +c.toFixed(1);
let e = +d.toFixed(1);
console.log(JSON.stringify(`value of f: ${f}`)); // "value of f: 0.3"
console.log(typeof f); //number
console.log(JSON.stringify(`value of d: ${d}`)); // "value of d: NaN"
console.log(typeof d); //number
console.log(JSON.stringify(`value of e: ${e}`)); // "value of e: NaN"
console.log(typeof e); //number
if (f !== e) return false;
// if (!Object.is(f, e)) return false;
return true;
}
console.log(calc('0.1', '0.2', '+', '0.3'));
Upvotes: 4
Views: 4555
Reputation: 3571
This is not an direct answer, but an explanation about toFixed()
and behavior of floating point numbers:
toFixed()
returns a text. One reason is, that generally no number exists, the can represent the result of toFixed()
. So use this function only for displaying, but not for calculating like here.
let f = +c.toFixed(1);
let e = +d.toFixed(1);
toPrecision(18)
is good to display all relevant digits of a number. Some examples:
(0.1).toPrecision(18) // => 0.100000000000000006
(0.2).toPrecision(18) // => 0.200000000000000011
(0.3).toPrecision(18) // => 0.299999999999999989
The examples explains, why 0.1+0.2
is not the same as 0.3
.
Same examples with toFixed(1)
:
(+(0.1).toFixed(1)).toPrecision(18) // => 0.100000000000000006
(+(0.2).toFixed(1)).toPrecision(18) // => 0.200000000000000011
(+(0.3).toFixed(1)).toPrecision(18) // => 0.299999999999999989
It changed nothing: toFixed(1)
formats the number, and the +
sign convert it back to the nearest existing number.
This is not a JavaScript issue, it is an issue of computer based floating points number using IEEE 754 (binary based numbers). Most hardware support this kind of floats.
In computer mathematics it is usual, to compare equality of floating point numbers by using absolute or relative delta values. Example for absolute delta:
function isEqual( num1, num2, epsilon )
{
return Math.abs( num1 - num2 ) <= epsilon;
}
isEqual( 0.1 + 0.2, 0.3, 1e-10 ) // => true
Databases support dataytpes like DECIMAL(5.1)
. Here 0.1+0.2 == 0.3
, because the use internally interger numbers and format only the output.
JavaScript example for currency with 2 fraction digits (euro,cent):
// scan user input:
cent = round( euro * 100 );
// adding cents:
cent3 = cent1 + cent2;
// print cents as euro
(cent/100).toFixed(2)+"€"
Upvotes: 1
Reputation: 8925
I run your code several times and there is no problem with it. I just found that the '0.3'
that you posted, it has an special character that looks like 3
but its not 3
. So, when you want to run it on JS it will show an error. So your solution was correct. Check here.
function calc(firstNumber, secondNumber, operation, resultInput) {
let a = parseFloat(firstNumber);
let b = parseFloat(secondNumber);
let aux = parseFloat(resultInput);
let r;
switch (operation) {
case '+':
r = a + b;
break;
case '-':
r = a - b;
break;
case '*':
r = a * b;
break;
case '/':
if (b !== 0) {
r = a / b;
} else {
r = 0;
}
break;
default:
console.log(`Sorry, wrong operator: ${operation}.`);
}
return (+r.toFixed(1)) === (+aux.toFixed(1));
}
console.log(calc('0.1', '0.2', '+', '0.3'));
Upvotes: 2
Reputation: 92460
Rather than converting back and forth to/from strings, you can create a function that tests if two numbers are close enough to be called equal. You decide some small delta and if the numbers are at least that close, you call it good.
function almost(a, b, delta = 0.000001){
return Math.abs(a - b) < delta
}
// not really equal
console.log("equal?", 0.2 + 0.1 === 0.3)
// but good enough
console.log("close enough?", almost(0.2 + 0.1, 0.3))
Upvotes: 4