Skip to content

Instantly share code, notes, and snippets.

@mastermatt
Last active August 29, 2015 13:56
Show Gist options
  • Save mastermatt/8870098 to your computer and use it in GitHub Desktop.
Save mastermatt/8870098 to your computer and use it in GitHub Desktop.
Returns the value of a float rounded using the number of decimal points provided as precision.
/**
* Returns the value of a float rounded using the number of decimal points provided as precision.
*
* Fixes binary rounding issues eg. Math.round(1.005 * 100) / 100 === 1 that present
* problems for accounting and finance-related software.
*
* The normal technique for rounding with decimals is to multiply the number by the log10 of the
* precision, round then divide by the same log eg. for 2 decimal points `round(num * 100) / 100`.
* The binary rounding issue, however, causes this: 1.005 * 100 === 100.49999999999999.
*
* This method plays on the use of converting scientific notation to a number,
* eg 1.005e2 === 100.5 so casting: `round(num + "e2") + "e-2"` to a number will result
* in the desired result.
*
* @param {number} number The value to round
* @param {int} precision The optional number of decimal digits to round to
*
* @returns {number} The adjusted value
*
* Based on MDN function
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
*/
Math.precisionRound = function( number, precision ) {
"use strict";
var nativeRound = Math.round,
castNumber = +number,
castPrecision = +precision,
scaledNumber, eSplit, eString;
// If the exp is undefined or zero, just use native rounding
if( typeof precision === "undefined" || 0 === castPrecision ) {
return nativeRound( number );
}
// If the value is not a number or the exp is not an integer...
if( isNaN( castNumber ) || !(typeof castPrecision === "number" && 0 === castPrecision % 1) ) {
return NaN;
}
// In case the number is already in scientific when casted to string, split off the exponent
eSplit = ("" + castNumber).split( "e" );
// Increase the exponent by the given precision and create a scientific notion string
eString = (eSplit[0] + "e" + (eSplit[1] ? (+eSplit[1] + castPrecision) : castPrecision));
// Cast to number and round
scaledNumber = nativeRound( +eString );
// Do the same as before, backwards
eSplit = ("" + scaledNumber).split( "e" );
eString = (eSplit[0] + "e" + (eSplit[1] ? (+eSplit[1] - castPrecision) : -castPrecision));
return +eString;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment