Number formatting with commas (v23)

Revision 23 of this benchmark created by Mike on


Description

to check the performance of regex over a native method Number.toLocaleString() and an array based implementation for number formatting in JavaScript.


Edit(v5):

  1. Reset the target number back to 343535353.53880934. (In v4, it was set to 343535353.53, which conceals the incorrect output of #6 numberWithCommas()).

  2. Add commafy2() that adds "," to the decimal part, too.

The result when tested with Chrome 28 on Linux. Note the incorrect output of numberWithCommas().

1. without regex   = 343,535,353.53880936   ops/sec= 192,524 
2. with regex      = 343,535,353.53880936   ops/sec= 303,569
3. toLocaleString  = 343,535,353.539        ops/sec=  42,805
4. commafy         = 343,535,353.53880936   ops/sec= 486,876
5. commafyandround = 343,535,353.54         ops/sec= 311,211
6. numberWithCommas= 343,535,353.53,880,936 ops/sec= 366,409 <== incorrect
7. commafy2        = 343,535,353.639,088,35 ops/sec= 351,064

Edit (v2):

  1. In previous jsperf, functions to be compared are defined inside test code. It means that it is not only comparing how fast those functions executes, but also how fast those functions are defined, which I believe is less interested to us. So they are moved to the Preparation code HTML section in this version.

  2. Add an event handler for each test-case to check if the outcome is correct. Turn on console to observe it.

  3. In all functions, use ''+num instead of num.toString() to speed it up.

  4. Extend the decimal part from .53 to .53880934 to see if that part can be handled correctly;

  5. Add an event handler to let console report results for easy copy/past.

  6. Add my function commafy. Using char scan, it shows very promising performance.

The result when tested with Chrome 28 on Linux:

1. without regex = 343,535,353.53880936 ops/sec= 123,534 
2. with regex    = 343,535,353.53880936 ops/sec= 192,796
3. toLocaleString= 343,535,353.539      ops/sec=  41,563
4. commafy       = 343,535,353.53880936 ops/sec= 409,303

# Note: Following the discussion in http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript

Preparation HTML

<script>
var number = 343535353.53880934, v;

function formatNumber(num) {
  var decimalPart = '';
  num = ''+num;
  if (num.indexOf('.') != -1) {
    decimalPart = '.' + num.split('.')[1];
    num = parseInt(num.split('.')[0]);
  }
  var array = (''+num).split('');
  var index = -3;
  while (array.length + index > 0) {
    array.splice(index, 0, ',');
    index -= 4;
  }
  return array.join('') + decimalPart;
};

function formatNumberRgx(num) {
  var parts = (''+num).split(".");
  parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  return parts.join(".");
};

function commafy( num){
  var parts = (''+num).split("."), s=parts[0], i=L= s.length, o='',c;
  while(i--){ o = (i==0?'':((L-i)%3?'':',')) 
                 +s.charAt(i) +o }
  return o+'.'+parts[1] 
}

// commafy2: add "," to decimal part, too
function commafy2(num){  
  var parts = (''+num).split("."), s=parts[0], i=L= s.length, o='',c;
  while(i--){ o = (i==0?'':((L-i)%3?'':',')) 
                 +s.charAt(i) +o }
  o+='.'; s= parts[1]; i=L=s.length;
  while(i--){ o += s.charAt(i) +(i==0?'':((L-i)%3?'':',')) 
                  }
  return o 
}

function commafyandround( num){
  var parts = num.toFixed(2).split("."), s=parts[0], i=L= s.length, o='',c;
  while(i--){ o = (i==0?'':((L-i)%3?'':',')) 
                 +s.charAt(i) +o }
  return o+'.'+parts[1] 
}


ui.events.start.push( function(){ 
                      console.log('\n\n Testing start, number='
                                 + number+'\n\n' ) } )

for( var i=0, tcase; tcase=ui.benchmarks[i]; i++ )
{
     if(window.console) tcase.events.complete=[
       function(){
                this.funcReturn = v; 
                console.log( '== test #'+ this.id
                + ' ('+ this.name + ') complete'
                + ', v= '+ v
                )
       }
     ]   
} 

ui.events.complete.push(
   function(){
      console.log('\n\n Result:\n'  );
      var tcases = ui.benchmarks;
      var out= Array( tcases.length );
      for( var i=0, tcase; tcase=tcases[i]; i++ )
      {
         console.log( tcase.name + ' = '
                    + tcase.funcReturn + ', ops/sec= '
                    + commafy( (''+tcase.hz).replace(/^\s*/,'')).split('.')[0]
                    )
      }
   }
)

function numberWithCommas(x) {
    return (""+x).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

function parts(num){
    var parts = [], str = num.toString(), curPart = '';
    for(var i = str.length - 1; i >= 0; i--){
        curPart = str[i] + curPart;
        if(curPart.length === 3 || i === 0){ parts.unshift(curPart); curPart = ''; }
    }
    return parts.join(',');
}

</script>

Test runner

Ready to run.

Testing in
TestOps/sec
1. without regex
v= formatNumber(number);
ready
2. with regex
v= formatNumberRgx(number);
ready
3. toLocaleString
v= number.toLocaleString();
ready
4. commafy
v= commafy(number);
ready
5. commafyandround
v= commafyandround(number);
ready
6. StackOverflow numberWithCommas
v= numberWithCommas(number);
ready
7. commafy2
v= commafy2(number);
ready
8. parts
v= parts(number);
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.