While reading the answers to Nathan Smith’s JavaScript Quiz ,
I was struck by the number of assumptions made around decimal precision. In two places, the author states “you need to
multiply values by 10
” when in actuality, you need to multiply values by the precision’s power of ten .
That lead me to write this bit of code to illustrate how you might handle high precision numbers (and of varying precision)
without using a separate library.
function getPrecision ( num ) { var split = ( num + "" ). split ( / ([\. e \-]{1,2}) / ); var i , len , token , next , nextNum , precision ; for ( precision = 0 , i = 1 , len = split . length ; i < len ; i += 1 ) { token = split [ i ]; next = split [ i + 1 ]; nextNum = + next ; if ( token === "." ) { precision += next . length ; } else if ( token === "e-" ) { precision += i > 1 ? nextNum + 1 : nextNum ; break ; } } return precision ; } function getMaxPrecision ( arr ) { var i , len , precision , max ; for ( max = 0 , i = 0 , len = arr . length ; i < len ; i += 1 ) { precision = getPrecision ( arr [ i ]); max = ( precision > max ) ? precision : max ; } return max ; } function sumArgs () { var args = Array . prototype . slice . call ( arguments ); var max = getMaxPrecision ( args ); var pow = Math . pow ( 10 , max ); var i , len , sum , arg ; for ( sum = 0 , i = 0 , len = args . length ; i < len ; i += 1 ) { arg = args [ i ]; sum += isNaN ( arg ) ? 0 : ( arg * pow ); } return sum / pow ; } console . assert ( getPrecision ( 1 ) === 0 ); //no decimal or e- console . assert ( getPrecision ( 1.1234567890 ) === 9 ); //zeros are dropped, just decimal console . assert ( getPrecision ( 1.1234567890123456 ) === 16 ); //longest without rounding console . assert ( getPrecision ( 0.1 ) === 1 ); console . assert ( getPrecision ( 0.12345678901234567 ) === 17 ); //longest without rounding console . assert ( getPrecision ( 0.0000000001 ) === 10 ); //no decimal, just e- console . assert ( getPrecision ( 0.00000000000000000000000000000000000000000000000000000123456789012345678 ) === 71 ); console . assert ( sumArgs ( 0.00002 , 0.00001 , 0.1 , 0.02 ) === 0.12003 ); console . assert ( getPrecision ( Math . min ()) === 0 ); //Infinity console . assert ( getPrecision ( 1 e - 324 ) === 0 ); console . assert ( getPrecision ( 1 e - 323 ) === 323 );
EDIT: My use of Math.pow made me curious if there was a faster way to build a big number, see the tests:
http://jsperf.com/make-a-big-number/2