I have had problems with the conditional 0.1 + 0.2 !== 0.3
. I tried 0.0001 + 0.0002
and this not equal 0.03
. As I know, we can use toFixed
to solve this problem. But is there any solution to resolve this problem dynamically? Because with 0.1 + 0.2
we use toFixed(2)
and 0.0001 + 0.0002
uses toFixed(4)
.

- 1,954
- 15
- 26
-
What is the higher level application? Usually you know how many digits are significant, like money is always 2 decimals. – Barmar Feb 11 '14 at 04:24
-
I just want to know the dynamically solution. I tried to create a lib to handle Float number. – ducdhm Feb 11 '14 at 04:45
4 Answers
You could extend Math to include a function to do the math. The below takes two floats, determines the greatest number of decimal places and then based on that does the math then does a toFixed using the greatest number of decimal places.
Math.addFloats = function (f1,f2){
//Helper function to find the number of decimal places
function findDec(dec){
var count = 0;
while(dec%1){
dec*=10;
count++;
}
return count;
}
//Determine the greatest number of decimal places
var dec1 = findDec(f1);
var dec2 = findDec(f2);
var fixed = dec1>dec2 ? dec1 : dec2;
//do the math then do a toFixed, could do a toPrecision also
var n = (f1+f2).toFixed(fixed);
return +n;
}
console.log( Math.addFloats(0.1,0.2) == 0.3 ); //evaluates to true
console.log( Math.addFloats(1/3,1/7) ); //prints 0.47619047619047616
console.log( 1/3 + 1/7 ); //prints 0.47619047619047616
Havent fully tested it but doing some preliminary tests shows it works dynamically, could probably modify it to do other maths, but would probably have to change the decimal count check when doing divides/multiples etc
NOTE: this does not seem to count decimal places well for e notation, ie 2e-14
results in like 30, when it should be 14
EDIT: changing the findDec function to this answers version of finding decimals places seems to be better at determining the correct number of decimal places for different types of numbers
function findDec(f1){
function isInt(n){
return typeof n === 'number' &&
parseFloat(n) == parseInt(n, 10) && !isNaN(n);
}
var a = Math.abs(f1);
f1 = a, count = 1;
while(!isInt(f1) && isFinite(f1)){
f1 = a * Math.pow(10,count++);
}
return count-1;
}

- 1
- 1

- 41,991
- 6
- 74
- 87
-
I converted this to C (because I cannot bring myself to run JavaScript) and tested it. It returns an incorrect answer for `Math.addFloats(.01, .23)`, because `findDec(.23)` returns 17. Attempting to “decimalize” binary floating-point this way is a bad approach. Multiplying `.23` by ten repeatedly produces numbers with fractional parts until just after 2300000000000000.5, at which point 23000000000000004 is produced. There are numerous other cases where it will fail. – Eric Postpischil Feb 11 '14 at 14:38
-
@EricPostpischil indeed, changed the findDec to one [from another answer](http://stackoverflow.com/a/20334744/560593) that seems to find the decimal places better, including those of e notation – Patrick Evans Feb 11 '14 at 15:38
-
@PatrickEvans: The new version fails with .0001 and .0637. (Results may vary in different language implementations because `pow` is often implemented poorly. Also, some implementations do not adhere well to IEEE-754 requirements.) **Decimalizing binary floating-point is a bad approach.** – Eric Postpischil Feb 11 '14 at 16:22
-
@EricPostpischil, ah ok, guess will have to look/work on finding a reliable way of doing it. probably could do it with string character counting but that's probably inefficient or just as error prone. – Patrick Evans Feb 11 '14 at 16:27
-
@PatrickEvans: String counting is the same approach: Attempting to decimalize binary floating-point. It is inherently impossible because, for example, both `0.1000000000000000055511151231257827021181583404541015625` and `.1` become 0.1000000000000000055511151231257827021181583404541015625 in 64-bit binary floating-point. Since there is only one result from two different inputs, it is impossible to determine, given the resulting value, which of the two inputs it came from. In other words, there is no way to know which number the user intended. The problem requires additional specifications. – Eric Postpischil Feb 11 '14 at 16:32
(a) if you are dealing with (say) dollars, the best sol'n is to do everything in cents, using ints. (This only works if you never deal with fractions of a penny.)
(b) in any language, treat doubles/floats as "a good approximation of the actual value", and never compare with ==
. Instead write a helper
double nearlyEqual(x,y,tolerance=0.00001) {
return abs(x-y) < tolerance*max(abs(x),abs(y)); }
(warning: untested code).

- 17,673
- 1
- 18
- 15
-
I think his question presumes that tolerance should depend on the inputs. – Barmar Feb 11 '14 at 04:25
Thank @Patrick so much. I have create a new code to add multiple float numbers based on your code. It's:
/**
* Find the number of decimal places
* @method findDec
* @param {Float|Number} dec
* @return {Number}
*/
var findDec = function (dec) {
var count = 0;
while (dec % 1) {
dec *= 10;
count++;
}
return count;
};
/**
* Find the greatest number of decimal places
* @method findFixed
* @param {Float|Number} dec
* @return {Number}
*/
var findFixed = function () {
var fixed = [];
for (var i = 0, arg; arg = arguments[i]; i++) {
fixed.push(findDec(arg));
}
return Math.max.apply(this, fixed)
};
/**
* Calculate total
* @method findFixed
* @param {Float|Number}
* @return {Float|Number}
*/
var calculate = function () {
var total = 0;
for (var i = 0, arg; arg = arguments[i]; i++) {
total += arg;
}
return total;
}
/**
* Add float number
* @method addNumber
* @param {Float|Number}
* @return {Float|Number}
*/
Math.addNumber = function() {
//Determine the greatest number of decimal places
var fixed = findFixed.apply(this, arguments);
var total = calculate.apply(this, arguments);
//do the math then do a toFixed, could do a toPrecision also
return +total.toFixed(fixed);
}

- 1,954
- 15
- 26
use Number.prototype with custom like this :
Number.prototype.lenDecimalPoint = function(val){
var decStr = val.toString().match(/\.\d*/);
if(decStr && decStr.length > 0) {
return decStr[0].replace().replace('.','').length;
} else {
return 0;
}
}
Number.prototype.getVal = function(val, stdDec10Val){
var dec10ValLog = Math.log10(stdDec10Val);
var thisValDecPoint = this.lenDecimalPoint(val);
var thisValStr = val.toString();
thisValStr = thisValStr.replace('/^0\./','');
thisValStr = thisValStr.replace('.','');
thisValStr += Math.pow(10 ,dec10ValLog - thisValDecPoint).toString().replace('1','');
return Number(thisValStr);
}
Number.prototype.getDec10Val = function(val1, val2){
var thisDecPoint = this.lenDecimalPoint(val1);
var newNumValDecPoint = this.lenDecimalPoint(val2);
var decPoint = thisDecPoint > newNumValDecPoint ? thisDecPoint : newNumValDecPoint;
return Math.pow(10,decPoint);
}
Number.prototype.add = function(newVal) {
newVal = Number(newVal)
var dec10Val = this.getDec10Val(this, newVal);
var thisIntVal = this.getVal(this, dec10Val);
var newIntVal = this.getVal(newVal,dec10Val);
return Number(((thisIntVal) + (newIntVal))/dec10Val);
}
Number.prototype.sub = function(newVal) {
newVal = Number(newVal)
var dec10Val = this.getDec10Val(this, newVal);
var thisIntVal = this.getVal(this, dec10Val);
var newIntVal = this.getVal(newVal,dec10Val);
return Number(((thisIntVal) - (newIntVal))/dec10Val);
}
Number.prototype.div = function(newVal) {
newVal = Number(newVal)
var dec10Val = this.getDec10Val(this, newVal);
var thisIntVal = this.getVal(this, dec10Val);
var newIntVal = this.getVal(newVal,dec10Val);
return Number(((thisIntVal) / (newIntVal)));
}
Number.prototype.mul = function(newVal) {
newVal = Number(newVal)
var dec10Val = this.getDec10Val(this, newVal);
var thisIntVal = this.getVal(this, dec10Val);
var newIntVal = this.getVal(newVal,dec10Val);
return Number((thisIntVal * newIntVal)/Math.pow(dec10Val,2));
}
usage:
(0.1).add(0.3)

- 203
- 1
- 9