I'm looking for a function in Javascript (or JQuery) to remove digits representing irrelevant precision from a number. I'm calculating an estimated probability, and it could be a range of values - and the exact decimal isn't very relevant - just the first non-zero decimal from the left.
Any thoughts on a good way to accomplish that?
That one is not as simple as it looks.
I assumed it has to also work on non-float and negative numbers. There also are the zero and the "Not a Number" cases.
Here is a robust solution.
function formatNumber(n) {
// in case of a string... ParseFloat it
n = parseFloat(n)
// Fool-proof case where n is "Not A Number"
if(isNaN(n)){return null}
// Negative number detection
let N = Math.abs(n);
let isNegative = N !== n;
// The zero case
if(N===0){
return n
}
// Numbers which do not need much processing
if(N>1){return +(n.toFixed(1))}
// Lets process numbers by moving the decimal dot to the right
// until the number is more than 1
let i = 0;
while (Math.floor(N) < 1) {
let dotPos = (""+N).indexOf(".") + 1
N = (""+N).replace(".","")
N = parseFloat(N.slice(0,dotPos)+"."+N.slice(dotPos))
i++;
}
// Re-add the negative sign
if (isNegative) {
N = -N;
}
// Now round and reposition the decimal dot
return Math.round(N) / Math.pow(10, i);
}
// ============================================================== Test cases
let testCases = [
{ in: 0.0001, out: 0.0001 },
{ in: 0.2453535, out: 0.2 },
{ in: 0.55, out: 0.6 },
{ in: 0.055, out: 0.06 },
{ in: 0.0055, out: 0.006 },
{ in: 0.15, out: 0.2 },
{ in: 0.015, out: 0.02 },
{ in: 0.0015, out: 0.002 },
{ in: 0.25, out: 0.3 },
{ in: 0.025, out: 0.03 },
{ in: 0.0025, out: 0.003 },
{ in: 0.00412, out: 0.004 },
{ in: -0.0045, out: -0.004 },
{ in: -0.55, out: -0.5 },
{ in: -0.15, out: -0.1 },
{ in: -0.015, out: -0.01 },
{ in: -0.0105, out: -0.01 },
{ in: -0.010504, out: -0.01 },
{ in: 2, out: 2 },
{ in: 2.01, out: 2.0 },
{ in: 2.34567, out: 2.3 },
{ in: 0, out: 0 },
{ in: "0.012%", out: 0.01 },
{ in: "Hello", out: null }
];
testCases.forEach((item) => {
let res = formatNumber(item.in);
let consoleMgs = `Test case: ${JSON.stringify(item)}\n Result: ${res}\n ${(res == item.out)?"PASS":"FAIL"}`
if (res == item.out) {
console.log(consoleMgs);
} else {
console.error(consoleMgs);
}
});
Notice that I "moved" the decimal dot using a string manipulation instead of multiplying by 10. That is because of this case:
//N = N * 10;
console.log(0.55*10)
console.log(0.055*10)
console.log(0.0055*10)
console.log(0.00055*10)
And that is due to the fact that the multiplication is done with binary numbers, internally.