Exact change hell

Exact change hell
0

#1

Spent all day trying to get the Exact change algorthm working only to be plagued by rounding errors because Javascript does not have a decimal or money datatype. (Unless I am mistaken).
This is a great reason for not using Javascript to do any kind of maths!!

Terrible. For anyone who doesn’t know what I am on about:


#2

The number object can be either a floating point or integer data value. Your problems aren’t caused by JavaScript.


#3

Technically, I think it’s always double precision floating point. But as long as it represents an integer within range, a floating point number is mathematically exact.

I had the same problem actually, so I converted all values to cents internally. Then I could deal with integers only (as you cannot give out fractional cents in change), and divided by 100 for display purposes only.


#4

Indeed it is!


#5

Another problem: I applying tried this code to the numbers:
cashReg.total = cashReg.total.toFixed(2);
This actually changes the number to a string!!!
This means that comparing numbers is now comparing strings.
So “Your problems aren’t caused by JavaScript.” maybe. But they are not helped.
There are languages and databases that handle money a lot better than JS.
C# has a Decimal datatype. Also in C# the automatic conversion to a string would not happen.
MySQL you can set the precision of the float.
Does JS have either of these?
So, my problems are caused by a deficiency in the language.
In the real world I would avoid doing this in JS.


#6

Known issue: See GitHub


#7

You would avoid doing what, exactly? Arithmetic? Calculating money? That’s going to severely limit your job prospects, my friend. Regardless of how it’s presented, floating point arithmetic has all the same limitations and is prone to the same errors in C#, C, MySQL, Python, Ruby, Scala, JavaScript, and every other language out there. Unless you’re dealing with many millions of dollars, JavaScript is entirely capable of doing the math you need it to. Follow foresterr’s advice and scale your calculations as needed.

.toFixed() and .toPrecision() return a string because the only reason you’d need these is for displaying a quantity on a page. Trailing zeros are there for humans, so 2.5 === 2.50.


#8

This challenge. Probably intentionally titeled “exact” change is supposed to teach you this lesson. It is the one thing you are supposed to learn from this.

If you demand exact number computations calculate using integer values. Which is trivial to do in this case and also the most common occurence of this problem.

Not only do floating point numbers have a finite precision but they are also stored in base 2 where a fraction like 1/10 is a periodic decimal number. This is not a javascript problem but the IEEE standard for storing decimal numbers in a computer. In general terms you only ever use floating point numbers if a close approximation is good enough of a result. If you can’t map your problem to integer values you look around for scientific libraries that can handle numbers in more versitaile ways.


#9

@JohnnyBizzel don’t let that discourage you though. If your Research landed you on the computerphile Video you did everything right.


#10

In C# / VB there are Double (float) and Decimal data types.
I should know, I’ve been a programmer for 7 years.
How decimal works in C#

By the way, I used a version of this to do the rounding:

var myNamespace = {};

myNamespace.round = function(number, precision) {
    var factor = Math.pow(10, precision);
    var tempNumber = number * factor;
    var roundedTempNumber = Math.round(tempNumber);
    return roundedTempNumber / factor;
};

Math.Round() as described here on Mozilla


#11

Glad to see you were able to get a solution to the problem (using JavaScript) by scaling the arithmetic, just as foresterr suggested.


#12

I think you are not mistaken. My conclusion is the same. In one of the test cases, my solution fails because anyhow my calculations are wrong by one cent.


#13

You can also do this:

change = Math.round(change * 100) / 100;

First you times by the number of decimal places you want (in this case 2, so by 100), and then you divide by the same to actually get it to two decimal places. Great little post about that here: http://www.javascriptkit.com/javatutors/round.shtml
Edit: Forgot to mention that the reason you would want to do this is in the case(s) when change < 1.00. If you simply did Math.round(), you would end up with 0. By multiplying by 100 and then dividing by 100, you would end up with 0.25, for example, instead of 0.