Truncate (not round off) decimal numbers in javascript
Solution 1
upd:
So, after all it turned out, rounding bugs will always haunt you, no matter how hard you try to compensate them. Hence the problem should be attacked by representing numbers exactly in decimal notation.
Number.prototype.toFixedDown = function(digits) {
var re = new RegExp("(\\d+\\.\\d{" + digits + "})(\\d)"),
m = this.toString().match(re);
return m ? parseFloat(m[1]) : this.valueOf();
};
[ 5.467.toFixedDown(2),
985.943.toFixedDown(2),
17.56.toFixedDown(2),
(0).toFixedDown(1),
1.11.toFixedDown(1) + 22];
// [5.46, 985.94, 17.56, 0, 23.1]
Old error-prone solution based on compilation of others':
Number.prototype.toFixedDown = function(digits) {
var n = this - Math.pow(10, -digits)/2;
n += n / Math.pow(2, 53); // added 1360765523: 17.56.toFixedDown(2) === "17.56"
return n.toFixed(digits);
}
Solution 2
Dogbert's answer is good, but if your code might have to deal with negative numbers, Math.floor
by itself may give unexpected results.
E.g. Math.floor(4.3) = 4
, but Math.floor(-4.3) = -5
Use a helper function like this one instead to get consistent results:
truncateDecimals = function (number) {
return Math[number < 0 ? 'ceil' : 'floor'](number);
};
// Applied to Dogbert's answer:
var a = 5.467;
var truncated = truncateDecimals(a * 100) / 100; // = 5.46
Here's a more convenient version of this function:
truncateDecimals = function (number, digits) {
var multiplier = Math.pow(10, digits),
adjustedNum = number * multiplier,
truncatedNum = Math[adjustedNum < 0 ? 'ceil' : 'floor'](adjustedNum);
return truncatedNum / multiplier;
};
// Usage:
var a = 5.467;
var truncated = truncateDecimals(a, 2); // = 5.46
// Negative digits:
var b = 4235.24;
var truncated = truncateDecimals(b, -2); // = 4200
If that isn't desired behaviour, insert a call to Math.abs
on the first line:
var multiplier = Math.pow(10, Math.abs(digits)),
EDIT: shendz correctly points out that using this solution with a = 17.56
will incorrectly produce 17.55
. For more about why this happens, read What Every Computer Scientist Should Know About Floating-Point Arithmetic. Unfortunately, writing a solution that eliminates all sources of floating-point error is pretty tricky with javascript. In another language you'd use integers or maybe a Decimal type, but with javascript...
This solution should be 100% accurate, but it will also be slower:
function truncateDecimals (num, digits) {
var numS = num.toString(),
decPos = numS.indexOf('.'),
substrLength = decPos == -1 ? numS.length : 1 + decPos + digits,
trimmedResult = numS.substr(0, substrLength),
finalResult = isNaN(trimmedResult) ? 0 : trimmedResult;
return parseFloat(finalResult);
}
For those who need speed but also want to avoid floating-point errors, try something like BigDecimal.js. You can find other javascript BigDecimal libraries in this SO question: "Is there a good Javascript BigDecimal library?" and here's a good blog post about math libraries for Javascript
Solution 3
var a = 5.467;
var truncated = Math.floor(a * 100) / 100; // = 5.46
Solution 4
You can fix the rounding by subtracting 0.5 for toFixed, e.g.
(f - 0.005).toFixed(2)
Solution 5
Consider taking advantage of the double tilde: ~~
.
Take in the number. Multiply by significant digits after the decimal so that you can truncate to zero places with ~~
. Divide that multiplier back out. Profit.
function truncator(numToTruncate, intDecimalPlaces) {
var numPower = Math.pow(10, intDecimalPlaces); // "numPowerConverter" might be better
return ~~(numToTruncate * numPower)/numPower;
}
I'm trying to resist wrapping the ~~
call in parens; order of operations should make that work correctly, I believe.
alert(truncator(5.1231231, 1)); // is 5.1
alert(truncator(-5.73, 1)); // is -5.7
alert(truncator(-5.73, 0)); // is -5
EDIT: Looking back over, I've unintentionally also handled cases to round off left of the decimal as well.
alert(truncator(4343.123, -2)); // gives 4300.
The logic's a little wacky looking for that usage, and may benefit from a quick refactor. But it still works. Better lucky than good.
Related videos on Youtube
Comments
-
kcssm almost 2 years
I am trying to truncate decimal numbers to decimal places. Something like this:
5.467 -> 5.46 985.943 -> 985.94
toFixed(2)
does just about the right thing but it rounds off the value. I don't need the value rounded off. Hope this is possible in javascript.-
Felix Kling over 13 yearsjQuery is just a framework and your problem is not jQuery related. It is more about doing some basic computation in JavaScript. I hope you are also satisfied with a non-jQuery solution.
-
MsTapp over 7 yearsI found it to be too much work to get my calculations to return just 2 decimals using Javascript. I was able to do it easily in my database view instead. I realize that this method won't fit every situation, but I want to put it out here because it might save somebody a lot of time.
-
-
Nick Knowlson over 12 yearsThis works well but will give results that are probably undesirable if he (or someone else looking at this answer later) has to deal with negative numbers. See stackoverflow.com/a/9232092/224354
-
Thomas W almost 12 yearsYeah, Prototypes don't work reliably cross-browser. Instead of defining this (limited purpose) function through the type system, in a way that doesn't work reliably, why not just put it in a library.
-
Thomas W almost 12 yearsWhy undesirable? Changing the direction of rounding when you go below 0, causes all sorts of arithmetical artefacts.
-
Thomas W almost 12 yearsWhy unexpected? Changing the direction of rounding when you go below 0, causes all sorts of arithmetical artefacts & crappy math. For example, twice as many numbers will round to 0, as any other integer. For graphics, accounting & many other uses, you'll get crappy results. To tell you the truth, it'd be harder to say what your suggestion is good for as to say what it's not.
-
Nick Knowlson almost 12 yearsIt's good for exactly what it says - when you want to truncate decimals rather than rounding.
-
shendz over 11 yearsThis does not work as excepted. Try the number 17.56 and digits = 2. It should be 17.56, but this function returns 17.55.
-
shendz over 11 yearsNot going to work with 17.56 because browser gives 17.56 * 100 = 1755.9999999999998 not 1756
-
kirilloid over 11 yearsThanks for report, this is an important notice. Unfortunately
17.56 != 17.55 - 0.01
. I updated the code. -
Nick Knowlson about 11 yearsGood point shendz. I've updated my answer with a solution that eliminates all floating-point error for those who need that.
-
Jeremy Witmer about 11 yearsThis won't work for numbers that are less than 1 if you want no decimals - truncateDecimals(.12345, 0) results in NaN unless you add a check:
if(isNAN(result) result = 0;
Depends on the behavior you want. -
Nick Knowlson about 11 yearsI didn't know what you were talking about at first, until I realized that ".1".toString doesn't always return "0.1", maybe only in Firefox's developer tools. I'll fix it, thanks.
-
Nick Knowlson almost 11 yearsThere is a difference between rounding and truncating. Truncating is clearly the behaviour this question is seeking. If I call
truncate(-3.14)
and receive-4
back, I would definitely call that undesirable. -
lukas.pukenis over 10 yearsif(isNan( is missing a closing bracket
-
Nick Knowlson over 10 yearsHeads up: as it is this doesn't work for very small numbers, numbers with more than 3 decimal places, or negative numbers. Try .0045, 5.4678 and -5.467
-
Hari Pachuveetil over 10 years+1. This does not handle integers (with no decimal point in them). Checking for
decPos
to be -1 will fix it. Like this:result = decPos === -1 ? num : numS.substr(0, 1 + decPos + digits);
-
Nick Knowlson about 10 yearsAha, so it doesn't. Small integers end up working fine, that's probably why I didn't notice back when I first wrote it. Fixed now, thanks!
-
Nick Knowlson about 10 yearsTwo inconsistencies with this function: This function returns a string so
1.11.toFixedDown(1) + 22
ends up as1.122
instead of23.1
. Also0.toFixedDown(1)
should produce0
but instead it produces-0.1
. -
rgajrawala almost 10 yearsNote that this function removes the negative sign. Ex:
(-10.2131).toFixedDown(2) // ==> 10.21
. -
rgajrawala almost 10 yearsAlso,
(1e-7).toFixedDown(0) // ==> 1e-7
. Does that for1e-(>=7)
(ex:1e-8
,1e-9
, ...). -
Liglo App over 9 yearsThis is the best answer. If you extend the
Math
prototype with this and check for NaN-s before executing, it would be just perfect. -
Alex Pi over 9 yearsThis is solution is good enough in my case, I only changed the regExp a little: "(-*\\d+\\.\\d{" + digits + "})(\\d)"
-
kirilloid over 9 yearsI'd write
"([-+]?\\d+\\.\\d{" + digits + "})(\\d)"
then. -
Admin almost 9 yearsI agree with Thomas. The difference in perspective may come with whether you are usually truncating for display, or for computation. From a computational perspective, this avoids "arithmetical artifacts"
-
ruffin almost 9 yearsGood call. Using a bitwise operator coerces the value into an int, and
or
ing with 0 means "just keep what I've already got". Does what my~~
answer does, but with a single bitwise operation. Though it has the same limitation as written too: We can't go over 2^31. -
Gene Parcellano over 7 yearsI like this solution, but just keep in mind that it's not fully supported by all browsers. (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…)
-
jperelli about 7 yearsHow does this work reference: stackoverflow.com/questions/7487977/…
-
Sanyam Jain over 6 years
var a = 65.1
var truncated = Math.floor(a * 100) / 100; // = 65.09
Hence this is not a correct solution -
Salman A over 6 yearsHow does it truncate something to, say, 2 decimal places?
-
dmarra about 6 yearsThis will work so long as you match the value you are subtracting with the length you wish to have. whatever you pass to toFixed() needs to be the number of 0's after the decimal.
-
Mike Makuch about 6 yearsThat doesn't work in all cases i.e. console.log(Math.trunc(9.28 * 100) / 100); // 9.27
-
zurfyx about 6 years@MikeMakuch that's not a problem with
Math.trunc
, but rather than9.28 * 100
is927.9999
rather than928
. You might want to read over The Perils of Floating Point -
giles123 almost 6 yearsTwo years after this was posted but stumbled across this when I was trying to work out the best way using Math.trunc, regex, etc. I really like this solution. Dead simple but works perfectly (for my use case anyway).
-
giles123 almost 6 yearsDon't forget to account for n=0 though.
-
jStaff over 5 yearsThis answer is obsurd. You shouldn't be editting the javascript Number class to remove decimal points. It complicates the issue a lot more than it has to be.
-
Ling Loeng over 5 years
x = 0.0000
testtruncateDecimals (x, 2)
fails. returns0
. not as expected0.00
-
Konstantin Smolyanin about 5 yearsIt seems that function works correctly but author's statement: "rounding bugs will always haunt you" is incorrect. There are also correct and more simple (and fast) solutions are provided in some other answers here.
-
Alex almost 5 yearsnot correct when
truncate((10 * 2.9) / 100);
this code return 0.28 instead of 0.29 jsfiddle.net/9pf0732d -
Alex almost 5 years
truncator((10 * 2.9) / 100, 2)
return 0.28 instead of 0.29 ... jsfiddle.net/25tgrzq1 -
ruffin almost 5 years@Alex As I'm guessing you realize... welcome to JavaScript!. There are fixes. Perhaps you'd like to share one? :D
-
Alex almost 5 years@ruffin I know about this issue =) I thought this answer was the solution to this problem. Unfortunately, I haven’t found an exact solution yet, everywhere there is such a problem.
-
Esteban over 4 yearsI'd write "([-+]?\\d*\\.\\d{" + digits + "})(\\d)" => Ex: .00 , .00009, .999999
-
iCode almost 4 yearsI like that this works with strings, thus eliminating the nuances of floating point numbers. Thanks!
-
taxilian over 3 yearsthis is the method that I would have expected to use
-
HelpfulPanda over 3 yearsNot working for me, truncator(1000.12345678, 7) returns 141.1299975
-
HelpfulPanda over 3 yearsWould love if that worked 100%, but Math.trunc(0.29 * Math.pow(10, 2)) / Math.pow(10, 2) gives you 0.28. To be honest the only reliable way I've seen so far is by splitting/cutting strings.
-
HelpfulPanda over 3 yearsSame issue as others pointed out, truncate(0.29, 2) gives you 0.28.
-
HelpfulPanda over 3 yearsconsole.log((-5).trim(2)); throws Uncaught TypeError: d[1] is undefined
-
ruffin over 3 years@Alex If you want to avoid floating point errors, use a decimal type. Or I guess we could rewrite to use string manipulation, but that seems insane. You'll notice similar answers have the same issue.
-
ruffin over 3 years@HelpfulPanda That's because JavaScript uses 32-bit ints for bitwise operators. The max 32-bit int is 2,147,483,647, and
100012345678
is significantly bigger than2147483647
. If you really have numbers larger than 32-bit (rather, if you need that many significant digits), this isn't the droid you're looking for. This is a quick and dirty (and fast) answer for, say, 4-5 sig digits. For potentially overkill-ish perfection, try the accepted answer. ;^D -
Ankur144 almost 3 yearsThis code will add padding "0" is places after decimal is than what is required.
-
Ankur144 almost 3 yearseg- for 3 decimal places (with padding"0") 5.3 --->5.300 0.01 ---->0.010 0.00001 --->0.000 5.32195--->5.321 -3.66696---> -3.666
-
stackoverflow about 2 years@HelpfulPanda pff yeah, wtf :| I'm currently having hard times solving this issue, so I think I'll fallback as you proposed to strings :)