Reputation: 9369
I know this question has been asked before but there are a few issues with the answers that have been given that I am trying to fix.
Here is one I found with the following answer:
function calc(number) {
var num = number;
var with2Decimals = num.toString().match(/^-?\d+(?:\.\d{0,2})?/)[0];
return with2Decimals
}
Here is another one I found that works in the same way that I like a little better:
function decimalFix(numToBeTruncated, numOfDecimals) {
var theNumber = numToBeTruncated.toString();
var pointIndex = theNumber.indexOf('.');
return +(theNumber.slice(0, pointIndex > -1 ? ++numOfDecimals + pointIndex : undefined));
}
For the most part they work nicely except for extremely large decimal numbers. For example, the number 0.00000001990202020291
reads as 1.99020202029e-8 in javascript so instead of giving me 0.000000019
if I wanted 9 decimal places in it would give me 1.99020202 which is extremely off base.
A lot of people have been really upset that I / other people would even want to tackle this issue because it seems unorthodox to them. It mostly has to do with currency and not loosing fractions of the value if I'm only allowed to be so exact in my calculations. For example there are times where I need to calculate micro transactions up to 6-8 decimal places but I'm loosing or gaining money in the process if I choose to round.
I appreciate any help or guidance someone can bring to this issue.
decimalFix(0.0000000199020202029,9) // 0.000000019
Upvotes: 0
Views: 85
Reputation: 12637
It mostly has to do with currency and not loosing fractions of the value if I'm only allowed to be so exact in my calculations. For example there are times where I need to calculate micro transactions up to 6-8 decimal places but I'm loosing or gaining money in the process if I choose to round.
//rounding to an arbitrary precision
function round(value, precision = 1) {
return Math.round(value / precision) * precision
}
console.log("round");
console.log(round(1.23456789, 0.001));
console.log(round(1.23456789, 1 / 4));
console.log(round(Date.now(), 1000*60*60)); //the current timestamp rounded to the hour
//rounding to a certain amount of decimal places
function roundToDecimalPlaces(value, decimalPlaces = 0) {
return round(value, "1e" + (-decimalPlaces));
}
console.log("roundToDecimalPlaces");
console.log(roundToDecimalPlaces(1.23456789, 4));
console.log(roundToDecimalPlaces(0.0000000199020202029, 9));
//and truncating everything after a certain amount of decimal places
function decimalFix(value, decimalPlaces = 0) {
let precision = "1e" + (-decimalPlaces);
return Math[value < 0 ? "ceil" : "floor"](value / precision) * precision;
}
console.log("decimalFix");
console.log(decimalFix(0.0000000199020202029, 9));
.as-console-wrapper {
top: 0;
max-height: 100%!important
}
But why do you want to limit the precision in the first place? If it's abount the view, and how the values are displayed, then this is the wrong approach.
Then you should check out Number#toFixed, Number#toPrecision and Number#toLocaleString
Upvotes: 0
Reputation: 1074138
You've said you need 6-8 digits to the right of the decimal, and 6-8 to the left of it (but typically more like 1-3).
That's riding the edge (if really 8.8, going over the edge) of what JavaScript's number type (IEEE-754 double-precision binary floating point) can do. Regardless of where the decimal is, JavaScript's floating point has roughly 15 decimal digits of precision before you lose information. (That's not the full story by any means.)
Good news: JavaScript is getting a BigInt
type you could use for this (by multiplying your values by 100,000,000 so they're whole numbers), details here. Until then, you might consider using one of the existing "big number" libraries.
If you need to use JavaScript's numbers, I would do it with whole numbers. With whole numbers, your perfectly-accurate range is -Number.MAX_SAFE_INTEGER - 1
through Number.MAX_SAFE_INTEGER + 1
. That's -9,007,199,254,740,992 through 9,007,199,254,740,992. With that, you can get seven digits left of the decimal and eight right of it (or of course, eight left and seven right). So if we're using eight digits of precision right of the decimal and seven to the left (well within limits), that's -9,999,999.99999999
through 9,999,999.99999999
.
Calculations would use Math.trunc
to remove the fractional portion (assuming you want to truncate toward zero). Output without scientific notation is trivial:
str = String(number);
str = str.substring(0, str.length - 8) + "." + str.substr(-8);
Examples:
function toString(number) {
var str = String(number);
str = str.substring(0, str.length - 8) + "." + str.substr(-8);
return str;
}
console.log(toString(999999999999999));
console.log(toString(123456712345678));
console.log(toString(765432187654321));
Upvotes: 3