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().

<span class="nu0">1</span>. <span class="me1">without</span> regex &nbsp; <span class="sy0">=</span> <span class="nu0">343</span><span class="sy0">,</span><span class="nu0">535</span><span class="sy0">,</span><span class="nu0">353.53880936</span> &nbsp; ops<span class="sy0">/</span>sec<span class="sy0">=</span> <span class="nu0">192</span><span class="sy0">,</span><span class="nu0">524</span> <br><span class="nu0">2</span>. <span class="kw1">with</span> regex &nbsp; &nbsp; &nbsp;<span class="sy0">=</span> <span class="nu0">343</span><span class="sy0">,</span><span class="nu0">535</span><span class="sy0">,</span><span class="nu0">353.53880936</span> &nbsp; ops<span class="sy0">/</span>sec<span class="sy0">=</span> <span class="nu0">303</span><span class="sy0">,</span><span class="nu0">569</span> <br><span class="nu0">3</span>. <span class="me1">toLocaleString</span> &nbsp;<span class="sy0">=</span> <span class="nu0">343</span><span class="sy0">,</span><span class="nu0">535</span><span class="sy0">,</span><span class="nu0">353.539</span> &nbsp; &nbsp; &nbsp; &nbsp;ops<span class="sy0">/</span>sec<span class="sy0">=</span> &nbsp;<span class="nu0">42</span><span class="sy0">,</span><span class="nu0">805</span> <br><span class="nu0">4</span>. <span class="me1">commafy</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">=</span> <span class="nu0">343</span><span class="sy0">,</span><span class="nu0">535</span><span class="sy0">,</span><span class="nu0">353.53880936</span> &nbsp; ops<span class="sy0">/</span>sec<span class="sy0">=</span> <span class="nu0">486</span><span class="sy0">,</span><span class="nu0">876</span> <br><span class="nu0">5</span>. <span class="me1">commafyandround</span> <span class="sy0">=</span> <span class="nu0">343</span><span class="sy0">,</span><span class="nu0">535</span><span class="sy0">,</span><span class="nu0">353.54</span> &nbsp; &nbsp; &nbsp; &nbsp; ops<span class="sy0">/</span>sec<span class="sy0">=</span> <span class="nu0">311</span><span class="sy0">,</span><span class="nu0">211</span> <br><span class="nu0">6</span>. <span class="me1">numberWithCommas</span><span class="sy0">=</span> <span class="nu0">343</span><span class="sy0">,</span><span class="nu0">535</span><span class="sy0">,</span><span class="nu0">353.53</span><span class="sy0">,</span><span class="nu0">880</span><span class="sy0">,</span><span class="nu0">936</span> ops<span class="sy0">/</span>sec<span class="sy0">=</span> <span class="nu0">366</span><span class="sy0">,</span><span class="nu0">409</span> <span class="sy0">&lt;==</span> incorrect <br><span class="nu0">7</span>. <span class="me1">commafy2</span> &nbsp; &nbsp; &nbsp; &nbsp;<span class="sy0">=</span> <span class="nu0">343</span><span class="sy0">,</span><span class="nu0">535</span><span class="sy0">,</span><span class="nu0">353.639</span><span class="sy0">,</span>088<span class="sy0">,</span><span class="nu0">35</span> ops<span class="sy0">/</span>sec<span class="sy0">=</span> <span class="nu0">351</span><span class="sy0">,</span>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:

<span class="nu0">1</span>. <span class="me1">without</span> regex <span class="sy0">=</span> <span class="nu0">343</span><span class="sy0">,</span><span class="nu0">535</span><span class="sy0">,</span><span class="nu0">353.53880936</span> ops<span class="sy0">/</span>sec<span class="sy0">=</span> <span class="nu0">123</span><span class="sy0">,</span><span class="nu0">534</span> <br><span class="nu0">2</span>. <span class="kw1">with</span> regex &nbsp; &nbsp;<span class="sy0">=</span> <span class="nu0">343</span><span class="sy0">,</span><span class="nu0">535</span><span class="sy0">,</span><span class="nu0">353.53880936</span> ops<span class="sy0">/</span>sec<span class="sy0">=</span> <span class="nu0">192</span><span class="sy0">,</span><span class="nu0">796</span> <br><span class="nu0">3</span>. <span class="me1">toLocaleString</span><span class="sy0">=</span> <span class="nu0">343</span><span class="sy0">,</span><span class="nu0">535</span><span class="sy0">,</span><span class="nu0">353.539</span> &nbsp; &nbsp; &nbsp;ops<span class="sy0">/</span>sec<span class="sy0">=</span> &nbsp;<span class="nu0">41</span><span class="sy0">,</span><span class="nu0">563</span> <br><span class="nu0">4</span>. <span class="me1">commafy</span> &nbsp; &nbsp; &nbsp; <span class="sy0">=</span> <span class="nu0">343</span><span class="sy0">,</span><span class="nu0">535</span><span class="sy0">,</span><span class="nu0">353.53880936</span> ops<span class="sy0">/</span>sec<span class="sy0">=</span> <span class="nu0">409</span><span class="sy0">,</span><span class="nu0">303</span> 

# 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.