Skip to content

Instantly share code, notes, and snippets.

@schowdhuri
Last active February 13, 2018 04:55
Show Gist options
  • Save schowdhuri/197d176e6afcdb45c9d5baa220f5b889 to your computer and use it in GitHub Desktop.
Save schowdhuri/197d176e6afcdb45c9d5baa220f5b889 to your computer and use it in GitHub Desktop.
Round Half-Even
//
// Round Half-Even
// Refer: https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
// Demo: https://codepen.io/schowdhuri/pen/wPXdJe
//
// Author: Subir Chowdhuri
// Updated: 2018-02-12
// License: MIT
//
// Copyright (c) 2018 Subir Chowdhuri
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
const isEven = d => d%2===0;
const round = (num, numDecimals=2) => {
if(typeof(num)!=="number")
throw new Error("Unrecognised format");
if(num < 0)
return -round(-num);
// convert to string; remove trailing 0s
const strNum = `${num}`.replace(/0+$/, "");
const decimalIndex = strNum.indexOf(".");
if(decimalIndex < 0) // no fractional part
return num;
let intPart = strNum.slice(0, decimalIndex);
if(intPart.length==0)
intPart = 0;
const fractPart = strNum.slice(decimalIndex + 1) // extract fractional part
if(fractPart.length < numDecimals)
return num;
const followingDig = parseInt(fractPart[numDecimals], 10);
if(followingDig < 5) {
// rounding not required
const newFractPart = fractPart.slice(0, numDecimals);
return parseFloat(`${intPart}.${newFractPart}`);
}
if(followingDig === 5) {
const newFractPart = fractPart.slice(0, numDecimals + 1);
if(parseInt(fractPart.slice(numDecimals + 1), 10) > 0) {
fractPart = `${newFractPart}9`;
} else {
fractPart = newFractPart;
}
}
let nextDig = parseInt(fractPart[fractPart.length-1], 10);
let carriedOver = 0;
for(let ptr=fractPart.length-1; ptr>=numDecimals; ptr--) {
let dig = parseInt(fractPart[ptr-1], 10) + carriedOver;
if(nextDig > 5 ||(nextDig==5 && !isEven(dig))) {
++dig;
}
if(dig > 9) {
dig -= 10;
carriedOver = 1;
} else {
carriedOver = 0;
}
nextDig = dig;
}
let newFractPart = "";
for(let ptr=numDecimals-2; ptr >=0; ptr--) {
let d = parseInt(fractPart[ptr], 10) + carriedOver;
if(d > 9) {
d -= 10;
carriedOver = 1;
} else {
carriedOver = 0;
}
newFractPart = `${d}${newFractPart}`
}
intPart = parseInt(intPart, 10) + carriedOver;
return parseFloat(`${intPart}.${newFractPart}${nextDig}`);
};
export default round;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment