Problematic Decimal Arithmetic in Javascript

It is a pretty well known fact that using javascript to add decimals 0.1 with 0.2 does not result in 0.3. [1] Try it yourself with the FireBug console. For the uninitiated, the problem stems from javascript’s internal representation of numbers. They are actually binary numbers that are usually exact, but sometimes for example, are 0.00000000000000004 off. This is particularly aggravating when writing calculators that rely on js to give accurate results.

In my text inputs I was using toFixed() and some magic HTML attributes to keep decimals nice and clean. However, this method breaks down when a user enters a number with more significant figures than initially set up or you try to operate on two numbers with different sig figs. It was probably inevitable that I use a little arithmetic library extending Number to make decimals play nice.

Since javascript is 13 years old I thought it would be a simple thing to find such a library. I was wrong. After four days learning, looking and lamenting I had no library. After putting this one together in about a day and a half I am not surprised that nobody published theirs. Most of the eleven functions are one-liners, yet it bothers me that there are probably thirty-odd implementations of the this out there and not one found through Google.

// decimal_arithmetic.js
String.prototype.digitsAfterDecimal = function()
{  var parts = this.split(".", 2);  // FIXME: Not international!
   if( ! parts[1] )
   {  parts[1] = "";  }
   return parts[1].length;
};
 
Number.prototype.biggerScalar = function(n)
{  return n.scale() > this.scale() ? n.scale() : this.scale();  };
 
Number.prototype.digitsAfterDecimal = function()
{  return this.toString().digitsAfterDecimal();  };
 
Number.prototype.divided = function(n)
{  return this.dividedBy(n);  };
 
Number.prototype.dividedBy = function(n)
{  return this.multiply( n.reciprocal() );  };
 
Number.prototype.minus = function(n)
{  return this.plus( n.negative() );  };
 
Number.prototype.multiply = function(n)
{  var s = this.biggerScalar(n);
   return (Math.round(s*this,0) * Math.round(s*n,0)) / (s*s);
};
 
Number.prototype.negative = function()
{  return -1 * this;  };
 
Number.prototype.plus = function(n)
{  var s = this.biggerScalar(n);
   return (Math.round(s*this,0) + Math.round(s*n,0)) / s;
};
 
Number.prototype.reciprocal = function()
{  return 1 / this;  };
 
Number.prototype.scale = function()
{  return Math.pow(10, this.digitsAfterDecimal() );  };

Now you can do magical things like:

0.1.plus(0.2)
// 0.3

yielding the correct results.

I am looking forward to javascript 2.0 when I can override the + operator. Maybe I won’t go that far since binary arithmetic is still faster.

[1] http://groups.google.com/group/comp.lang.javascript/…

2 thoughts on “Problematic Decimal Arithmetic in Javascript”

  1. Edited times and plus to use Math.round.

    Looks like using this with prototype is going to clash with Number.times()

    [WORDPRESS HASHCASH] The poster sent us ‘0 which is not a hashcash value.

  2. Edited Number.times() and Number.plus() to use Math.round().

    Looks like using this and prototype causes a namespace clash around Number.times(). Renaming to Number.multiply().

    [WORDPRESS HASHCASH] The poster sent us ‘0 which is not a hashcash value.

Leave a Reply