简体   繁体   中英

javascript correctly rounding up to two decimals, impossible?

In php, we have number_format() . Passing it a value such as:

number_format(3.00 * 0.175, 2);

returns 0.53 , which is what I would expect.

However, in JavaScript using toFixed()

var num = 3.00 * 0.175;
num.toFixed(2);

returns 0.52 .

Ok, so perhaps toFixed is not what I want... Maybe something like this...

var num = 3.17 * 0.175;
var dec = 2;
Math.round( Math.round( num * Math.pow( 10, dec + 1 ) ) / Math.pow( 10, 1 ) ) / Math.pow(10,dec);

No, that doesn't work either. It will return 0.56.

How can I get a number_format function in JavaScript that doesn't give an incorrect answer?

Actually I did find an implementation of number_format for js, http://phpjs.org/functions/number_format , but it suffers from the same problem.

What is going on here with JavaScript rounding up? What am I missing?

JavaScript does badly with floating point numbers (as do many other languages).

When I run

3.000 * 0.175

In my browser, I get

0.5249999999999999

Which will not round up to 0.525 with Math.round . To circumvent this, you kind of have to multiply both sides until you get them to be integers (relatively easy, knowing some tricks help though).

So to do this we can say something like this:

function money_multiply (a, b) {
    var log_10 = function (c) { return Math.log(c) / Math.log(10); },
        ten_e  = function (d) { return Math.pow(10, d); },
        pow_10 = -Math.floor(Math.min(log_10(a), log_10(b))) + 1;
    return ((a * ten_e(pow_10)) * (b * ten_e(pow_10))) / ten_e(pow_10 * 2);
}

This may look kind of funky, but here's some pseudo-code:

get the lowest power of 10 of the arguments (with log(base 10))
add 1 to make positive powers of ten (covert to integers)
multiply
divide by conversion factor (to get original quantities)

Hope this is what you are looking for. Here's a sample run:

3.000 * 0.175
0.5249999999999999

money_multiply(3.000, 0.175);
0.525

Why all the powers?? Why not just add slightly less than 1/2 a cent and round :

(3.00 * 0.175 + 0.0049).toFixed(2)

Never had any accountants complain about the output.

The toFixed function is working correctly. It truncates past the specified amount of fraction digits.

I think the problem you are encountering is with floating point math as opposed to the rounding itself.

Using the firebug console for testing, logging the result of 3.00 * 0.175 given 0.524999... . So rounding this number down is actually correct.

I don't know if there is a good solution to your problem, but in my experience when working with currency: it is easier to work in the smallest unit (cents) and then convert for display.

I know this is old but this is how I usually solve a rounding problem. This can be put in a function easily but for now I just put in simple vars for right now. If this doesn't work you could use money_format() or number_format() as a start from php.js (more info below).

var n = (3.00 * 0.175);
n = Math.round(n * Math.pow(10, 3)) / Math.pow(10, 3);
Math.round(n*100)/100;

comes out to 0.53 (0.5249999999999999)

var n = (3.00 * 0.175);
n = Math.round(n * Math.pow(10, 3)) / Math.pow(10, 3);
Math.round(n*100)/100;

comes out to 0.56 (0.55475)

It also looks like the php.js repo is being kept up on GitHub https://github.com/kvz/phpjs so if there isn't a function that is not performing correctly an issue can be submitted.

Anyway figured this information may help someone looking later on.

Why didn't you just use Math.round( num * Math.pow( 10, dec ) ) / Math.pow( 10, dec) ) ?

EDIT: I see, the problem is that 3 * 0.175 gives you 0.52499999999999991, leading you to want an additional rounding step. Maybe just adding a small amount would work:

Math.round( num * Math.pow( 10, dec ) + 0.000000001 ) / Math.pow( 10, dec) )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM