I have this problem with rounding up or rounding off with javascript. Well it gets the job done but there are some scenarios that it fails to do the job properly.
For example.
The price is 0.30 with a 15% discount should have 0.255 or 0.26 as result. (this is somehow okay)
The price is 79.99 with a 50% discount should have 40 as result. (actual result is 39.995)
Tried using this as reference Using toFixed(2) and math round to get correct rounding
also did this process Math.round(value*Math.pow(10,dec))/Math.pow(10,dec);
Any ideas?
Thanks
This is my initial test. Though this is just an initial test what I did is checked if the last two digits is greater than 5 then apply Math.ceil that would to the 0.255 to be 0.26 but when using 39.99 the result would still be 39.99 though 39.99 should use Math.round so that the result would be 40.
What I would like to accomplish is to be able to round up or round down any number if it needs rounding up or if it needs rounding up.
UPDATE
I was able to solve my own problem. (Atleast to my specific environment)
http://jsfiddle.net/xMj3M/4/
This is because of how floats are handled. I would suggest having your original numbers in integers only, so you would start with 7999. If you use this method, you will get the correct result.
In general, especially when dealing with money, it is always a good idea to work in pennies first, and then output it in the format of the currency.
You could try:
var someNumber = 36.7327;
var someNumberRoundedTo2Decimals = (Math.round((someNumber * 100)) / 100);
// = 36.73
basically this moves the decimal to the right 2 digits..
3673.27
then rounds it off to an int
3673
then moves the decimal back to the left 2 places
36.73
Related
I am aware of the fact that floating point calculation is likely to be inaccurate. For example:
var x = 100*0.333;
Here x gets set to 33.300000000000004 rather than 33. This might seem to be a minor problem, but it becomes serious if rounding is involved. For example, Math.ceil(x) will be incorrect - will yield 34 rather than 33.
The problem is that I was told that JS doesn't make much difference between integers and floating points. So I'm becoming worried if there's any way to ensure that integral division is correct.
The usual formula for integer division in JS seems to be Math.floor(n/m), where n, m are integers. So now let's say that m divides n evenly. Since JS treats every number as if it was a floating point number, n/m will return a floating point number. If this floating point number is not exact, but is even a very little bit smaller than the actual calculation result, then Math.floor(n/m) will get rounded down to one integer below it should.
To illustrate: if, for example, 9/3 yields 2.999999999999999 rather than 3, then Math.floor(9/3) will incorrectly yield 2 rather than 3. Well, actually, Math.floor(9/3) does give out correct results, but this is of course only an example; substitute 9 and 3 with any unfortunate choice of integers.
Can such scenario ever happen? If so, is there any way to ensure correct results?
Can such scenario ever happen?
No. If they are integers (i.e. floating numbers with no fractional part) and evenly divide, then the result will be exact and an integer again.
If the don't evenly divide, the result will always be greater than the exact result from integer division. You'll never get a 2.9999999… number that was supposed to be a 3. The floating point results are as precise as representable, rounding does not destroy the total preorder.
if you want an exact integral division result. Then do this
Math.round((m - m%n)/n)
If the value of dividing two numbers is supposed to be 3 then it will be. If the value is supposed to be 2.999999999999, it will be. Java doesn't do some kind of strange hidden rounding, up or down. If you ALWAYS need to have the value round down then Use Floor(x/y) Even if the value is 2.99999999999999999999999999999999999 it will still come back as 2. That's what Floor does. Same thing with celling. if the value is 3.0000000000000000000001 then it will be set to 4. I teach this in college, this is how it's supposed to and does work.
I have an odd problem. I'm grabbing numbers from the web audio API, what I get out is a constant list of:
3.1051260268969075e-28 etc etc
I want to round that figure, but using:
Math.round(magAmount);
Always returns 0. magAmount is the variable I store the number in. Another odd thing, if I multiply the number by 5 I get a value lower than the original, in this case 1.5525630134484537e-27.
Any ideas?
Thanks.
That number is 5 times bigger.
e-28 is smaller than e-27
Note that e-28 is *10^-28, so there's nothing strange in your code.
And since this number is tiny, rounding it will certainly returns a 0.
Math.round() will round to the next integer, so rounding 0.0000000000..00001 (which you are doing) will round to 0.
also e-27 is bigger than e-28, so your multiply is correct
Round to what precision? If you want
3.1051260268969075e-28 -> 3.1e-28 then
Math.round(3.1051260268969075e-28 * 1.0e+29) * 1.0e-29
I am adding client-side sub-total calculations to my order page, so that the volume discount will show as the user makes selections.
I am finding that some of the calculations are off by one cent here or there. This wouldn't be a very big deal except for the fact that the total doesn't match the final total calculated server-side (in PHP).
I know that the rounding errors are an expected result when dealing with floating point numbers. For example, 149.95 * 0.15 = 22.492499999999996 and 149.95 * 0.30 = 44.98499999999999. The former rounds as desired, the latter does not.
I've searched on this topic and found a variety of discussions, but nothing that satisfactorily addresses the problem.
My current calculation is as follows:
discount = Math.round(price * factor * 100) / 100;
A common suggestion is to work in cents rather than fractions of dollars. However, this would require me to convert my starting numbers, round them, multiply them, round the result, and then convert it back.
Essentially:
discount = Math.round(Math.round(price * 100) * Math.round(factor * 100) / 100) / 100;
I was thinking of adding 0.0001 to the number before rounding. For example:
discount = Math.round(price * factor * 100 + 0.0001) / 100;
This works for the scenarios I've tried, but I am wondering about my logic. Will adding 0.0001 always be enough, and never too much, to force the desired rounding result?
Note: For my purposes here, I am only concerned with a single calculation per price (so not compounding the errors) and will never be displaying more than two decimal places.
EDIT: For example, I want to round the result of 149.95 * 0.30 to two decimal places and get 44.99. However, I get 44.98 because the actual result is 44.98499999999999 not 44.985. The error is not being introduced by the / 100. It is happening before that.
Test:
alert(149.95 * 0.30); // yields 44.98499999999999
Thus:
alert(Math.round(149.95 * 0.30 * 100) / 100); // yields 44.98
The 44.98 is expected considering the actual result of the multiplication, but not desired since it is not what a user would expect (and differs from the PHP result).
Solution: I'm going to convert everything to integers to do my calculations. As the accepted answer points out, I can simplify my original conversion calculation somewhat. My idea of adding the 0.0001 is just a dirty hack. Best to use the right tool for the job.
I don't think adding a small amount will favor you, I guess there are cases where it is too much. Also it needs to be properly documented, otherwise one could see it as incorrect.
working in cents […] would require me to convert my starting numbers, round them, multiply them, round the result, and then convert it back:
discount = Math.round(Math.round(price * 100) * Math.round(factor * 100) / 100) / 100;
I think it should work as well to round afterwards only. However, you should first multiply the result so that the significant digits are the sum of the two sig digits from before, i.e. 2+2=4 decimal places in your example:
discount = Math.round(Math.round( (price * factor) * 10000) / 100) / 100;
Adding a small amount to your numbers will not be very accurate. You can try using a library to get better results: https://github.com/jtobey/javascript-bignum.
Bergi’s answer shows a solution. This answer shows a mathematical demonstration that it is correct. In the process, it also establishes some bound on how much error in the input is tolerable.
Your problem is this:
You have a floating-point number, x, which already contains rounding errors. E.g., it is intended to represent 149.95 but actually contains 149.94999999999998863131622783839702606201171875.
You want to multiply this floating-point number x by a discount value d.
You want to know the result of the multiplication to the nearest penny, performed as if ideal mathematics were used with no errors.
Suppose we add two more assumptions:
x always represents some exact number of cents. That is, it represents a number that has an exact number of hundredths, such as 149.95.
The error in x is small, less than, say, .00004.
The discount value d represents an integer percentage (that is, also an exact number of hundredths, such as .25 for 25%) and is in the interval [0%, 100%].
The error is d is tiny, always the result of correct conversion of a decimal numeral with two digits after the decimal point to double-precision (64 bit) binary floating point.
Consider the value x*d*10000. Ideally, this would be an integer, since x and d are each ideally multiples of .01, so multiplying the ideal product of x and d by 10,000 produces an integer. Since the errors in x and d are small, then rounding x*d*10000 to an integer will produce that ideal integer. E.g., instead of the ideal x and d, we have x and d plus small errors, x+e0 and d+e1, and we are computing (x+e0)•(d+e1)•10000 = (x•d+x•e1+d•e0+e0•e1)•10000. We have assumed that e1 is tiny, so the dominant error is d•e0•10000. We assumed e0, the error in x, is less than .00004, and d is at most 1 (100%), so d•e0•10000 is less than .4. This error, plus the tiny errors from e1, are not enough to change the rounding of x*d*10000 from the ideal integer to some other integer. (This is because the error must be at least .5 to change how a result that should be an integer rounds. E.g., 3 plus an error of .5 would round to 4, but 3 plus .49999 would not.)
Thus, Math.round(x*d*10000) produces the integer desired. Then Math.round(x*d*10000)/100 is an approximation of x*d*100 that is accurate to much less than one cent, so rounding it, with Math.round(Math.round(x*d*10000)/100) produces exactly the number of cents desired. Finally, dividing that by 100 (to produce a number of dollars, with hundredths, instead of a number of cents, as an integer) produces a new rounding error, but the error is so small that, when the resulting value is correctly converted to decimal with two decimal digits, the correct value is displayed. (If further arithmetic is performed with this value, that might not remain true.)
We can see from the above that, if the error in x grows to .00005, this calculation can fail. Suppose the value of an order might grow to $100,000. The floating-point error in representing a value around 100,000 is at most 100,000•2-53. If somebody ordered one hundred thousand items with this error (they could not, since the items would have smaller individual prices than $100,000, so their errors would be smaller), and the prices were individually added up, performing one hundred thousand (minus one) additions adding one hundred thousand new errors, then we have almost two hundred thousand errors of at most 100,000•2-53, so the total error is at most 2•105•105•2-53, which is about .00000222. Therefore, this solution should work for normal orders.
Note that the solution requires reconsideration if the discount is not an integer percentage. For example, if the discount is stated as “one third” instead of 33%, then x*d*10000 is not expected to be an integer.
When I pull the values I want to multiply, they're strings. So I pull them, parse them as floats (to preserve the decimal places), and multiply them together.
LineTaxRate = parseFloat(myRate) * parseFloat(myQuantity) * parseFloat(myTaxRateRound);
This has worked for 99% of my invoices but I discovered one very odd problem.
When it multiplied: 78 * 7 * 0.0725
Javascript is returning: 39.584999999999994
When you normally do the math in a calculator its: 39.585
When all is said and done, I take that number and round it using .toFixed(2)
Because Javascript is returning that number, it's not rounding to the desired value of: $39.59
I tried Math.round() the total but I still get the same number.
I have thought of rounding the number to 3 decimals then two, but that seems hacky to me.
I have searched everywhere and all I see is people mention parseFloat loses its precision, and to use .toFixed, however in the example above, that doesn't help.
Here is my test script i made to recreate the issue:
<script>
var num1 = parseFloat("78");
var num2 = parseFloat("7");
var num3 = parseFloat("0.0725");
var myTotal = num1 * num2 * num3;
var result = Math.round(myTotal*100)/100
alert(myTotal);
alert(myTotal.toFixed(2));
alert(result);
</script>
Floating points are represented in binary, not decimal. Some decimal numbers will not be represented precisely. And unfortunately, since Javascript only has one Number class, it's not a very good tool for this job. Other languages have decent decimal libraries designed to avoid precisely this kind of error. You're going to have to either accept one-cent errors, implement a solution server-side, or work very hard to fix this.
edit: ooh! you can do 78 * 7 * 725 and then divide by 10000, or to be even more precise just put the decimal point in the right place. Basically represent the tax rate as something other than a tiny fraction. Less convenient but it'll probably take care of your multiplication errors.
You might find the Accounting.js library useful for this. It has an "improved" toFixed() method.
JavaScript/TypeScript have only one Number class which is not that good. I have the same problem as I am using TypeScript. I solved my problem by using decimal.js-light library.
new Decimal(78).mul(7).mul(0.0725) returns as expected 39.585
hello I want to round an amount in javascript but unable to do and totally stuck.
My scenario is, I have a base amount and marukup % on base. On the basis of these two I want to calculate total amount. I have calculated the amount but i want to round the result.
I have used following formula
amount=base+((base/100)*markup
This formula always give me result without decimal point. I want to get exact amount upto two decimal points. I have used math.round like this
amount=math.round(base+((base/100)*markup).toFixed(2)
but it always return result without decimal point. For example my base value is 121 and markup is 5%. The amount should be 127.05 . But above formula always returns 127. Any guidelines?
I'm pretty sure math.round returns an integer. Even if you round it then, it'll just be 127.00 anyway.
Here's the correct solution(but it isn't easy):
Do not use non-integer values for money!
It doesn't work.
Use an integer in cents.
That is, instead of 127, keep 12700 in your app.
That way all roundings should work fine.
The toFixed(n) function rounds the Number to n decimals, there is no need to use Math.round at all. Try:
total = function (base, markup) { return (base + (base * markup / 100)); };
amount = total(121,5).toFixed(2);
Note that amount will be typeof String and not Number.
This should work:
amout = (Math.round((base*(1+markup))*100)/100).toFixed(2)
By the way, i was using markup as 5/100...
Sound like a integer division problem to me. I'd guess that javascript is seeing the 100 as an int.
Try this:
amount=(base+((base/100.toFixed(2))*markup).toFixed(2)