Exact Change exercise, why the floating point error and best approach to subtract integer from floating point?

Hey everyone!

I’m working on the exact-change exercise. I’ve got a couple of tests passing, however when running my solution against the following test:

checkCashRegister(
  3.26,
  100.00,
  [
    ["PENNY", 1.01], 
    ["NICKEL", 2.05], 
    ["DIME", 3.10], 
    ["QUARTER", 4.25], 
    ["ONE", 90.00], 
    ["FIVE", 55.00], 
    ["TEN", 20.00], 
    ["TWENTY", 60.00], 
    ["ONE HUNDRED", 100.00]
  ]
);

for which the expected result is:

[
  ["TWENTY", 60.00], 
  ["TEN", 20.00], 
  ["FIVE", 15.00], 
  ["ONE", 1.00], 
  ["QUARTER", 0.50], 
  ["DIME", 0.20], 
  ["PENNY", 0.04]
]

or as a number: 96.74

I encounter some funny behaviour which makes the test fail and my result is one penny short of a correct answer for the test to pass.

My solution calculates the change due (in this case 96.74), then uses reduce to produce an array of the change. During the reduce code I subtract each unit put in the change array from a changeDue variable to keep track of how much more I need to give until it reaches 0. When outputting this changeDue variable to the console I get the following…

changeDue 96.74 // -20
changeDue 76.74 // -20, all okay
changeDue 56.739999999999995 // -20, what happened here?!
changeDue 36.739999999999995 // -20, seems to have subtracted 20 from the last number okay?
changeDue 26.739999999999995
changeDue 16.739999999999995
changeDue 11.739999999999995
changeDue 6.739999999999995
changeDue 1.7399999999999949
changeDue 0.7399999999999949
changeDue 0.4899999999999949
changeDue 0.23999999999999488
changeDue 0.13999999999999488
changeDue 0.03999999999999487
changeDue 0.02999999999999487
changeDue 0.01999999999999487
changeDue 0.009999999999994869

Now I’m guessing it’s some issue to do with subtracting an integer/whole-number from a floating-point number and a quick google just now shows it can probably be solved by converting the floating point to integer (see http://stackoverflow.com/questions/10713878/decimal-subtraction-problems-in-javascript), but I’d like to know:

  1. Why this only happened on the second subtraction and not the first?
  2. The best way to tackle this issue (is it simply floating point * 10 then divide result by 10)?

Thanks in advance to anyone who can help!

2 Likes

@haakym

Interesting situation.

First:
###What is a floating point?
From wikipedia:

… floating point is the formulaic representation that approximates a real number so as to support a trade-off between range and precision. A number is, in general, represented approximately to a fixed number of significant digits (the significand) and scaled using an exponent in some fixed base…

The term floating point refers to the fact that a number’s radix point (decimal point, or, more commonly in computers, binary point) can “float”; that is, it can be placed anywhere relative to the significant digits of the number. This position is indicated as the exponent component, and thus the floating-point representation can be thought of as a kind of scientific notation.

A floating point is a sort of scientific notation used to represent decimals. It is subject to higher or lower precision based on the amount of bits you use to represent those numbers. This is why a 64-bit machine is considered more precise than a 32-bit machine.

###Why Floating Point Numbers?

Computers are not capable to create fractions: their architecture is DISCRETE, which is closer to Integer types (remember: it is actually BINARY). CS needed a trick to approximate advanced numbers that are closer to a CONTINUOUS abstraction from a discrete platform.

Floating Point numbers is probably the most known trick.

###Back to your problem…

You asked:

Why this only happened on the second subtraction and not the first?

In all the cases you can also say that the floating point notation is an approximation. This is important. Even if you got a value that clearly represent the one you are looking for (the first changeDue), the value you are getting is still an approximation. Your computer is converting all the time the value into an “integer” (actually into a binary) in memory, and use the opposite trick to convert that value saved in memory back into a Real output. Any operation you practice on it is on that approximated value that exists in memory. It is important for you to understand that a floating point number is NOT a Real number itself: it is a trick to represent Reals in the computer binary memory.

###How to deal with problems when you have a fixed number of decimals?
There are several ways. Sometimes some languages are aware of the issue and offer some options. Python, for example, would offer a consequent type called decimal data type. In JS, you could find libraries that will help you with that, for example: jsdecimal - npm.

You also already mentioned one: deal with them as if you would be working with integers instead. A similar reference to that one you provided is the following: JavaScript: Decimal Values - Stack Overflow.

Here another blog with some code you can refactor:
http://www.learnwebprogramming.info/2016/06/effective-javascript-floating-point.html

There are more advanced tricks, used for comparisons, when you can not easily convert the value into a more discrete form (eg. much more decimals).

For more info about where to get a more general explanation about the floating point error as well as some suggested ways to solve it, check also: The Floating-Point Guide - Basic Answers

###Other FCC campers with similar problems?
Your problem seems to be a common one here in FCC. Check other threads to see if you find better explanations? Ask directly to other campers if in doubt. Eg of other threads:

###Final note: A Bug or not a Bug?
I would say: don’t see it as a bug by FCC!!! It is in fact a “bug”, but one that you might have to deal more frequently that you think: it is the nature of the computer, as I explained above. The more precision you require, the more frequent the problem. So use this exercise to apply one way to cope with it when having a simple problem.

4 Likes

Save yourself a lot of grief with money and javascript in general and always multiply all your numbers by one hundred at the beginning so you are working with cents. Then divide by one hundred at the end. Will save you lots of hair pulling and scotch.

Thanks for your response! I seem to have solved my immediate issue, but I don’t have a full grasp on the why yet, I’m sure going through the links you provided will remedy that. Thanks again!

Thanks for your reply. That’s the approach I ended up taking (for now), however, to be more specific I actually found I can’t simply multiply all numbers by 100 as this causes the same issue at hand.

What I found worked was to multiply the floating point number by 10 then multiply the result by 10 again.

For example, check the following code which multiplies by 100…

[
    ["NICKEL", 2.05]
].map(item => [item[0], item[1]*100])

// outputs:

[
    ["NICKEL", 204.99999999999997]
]

however multiplying by 10 twice seems to work okay…

[
    ["NICKEL", 2.05]
].map(item => [item[0], (item[1]*10)*10 ])

// outputs:

[
    ["NICKEL", 205]
]

Probably not an ideal solution, but it works for now until I find a better solution or have a better grasp on the issue.

Thanks for your help guys!

Just to offer another solution, using round can be quite handy in situations like these when you know you only care about the value to a specific number of decimal places.

1 Like

Hi @haakym,

Yes… as I said floating point is an approximation technique to a Real number with decimals (CONTINUOUS) when representing them using the DISCRETE architecture of a computer. It is not itself the number. It is understood as TYPE in computers because it is indeed a type, but notice they are not called REALS or RATIONALS, it is the FLOAT type.

Here a brief introduction about binary numbers. This is the sort of conversion the computer does. Notice that in the computer they have FINITE length (some computers have 32-bits to represent ALL the existing numbers as binaries; other ones have 64-bits).

Why?. Well, bear in mind that for now there is no simple architecture to represent ALL the infinite values you could imagine!

Also important: this doesn’t have to do with the language in many ways: it is mostly the LIMITS to formulate a SOUNDING COMPUTER MEMORY ARCHITECTURE flexible enough to approximate ALL THE EXISTING NUMBERS in the world!!

So all numbers with decimals are indeed APPROXIMATIONS, even 0.0. As soon as you use the “.” to separate them, you are calling the floating point approximation or any other implementation in use.

The solution you are referring is one when you know the number of decimals you have and that you will have EXACTLY the same number of decimals for each operation (eg. sums): you are converting the “REAL” value of xxx.dd into an INTEGER:

xxx.dd * 100 = xxxdd //converting a "float" into an integer form by multiplying by 100 (NOT 100.0!)

The computer will have less problems working with integers because they fit very well into the binary discrete architecture of the computer, which DON’T REALLY UNDERSTAND RATIONALS (ie. FRACTIONS, DECIMALS) very well and requires some additional tricks.

Rounding, as mentioned by @CronoxAU, is also an option BUT depending of the problem you have you could introduce a rounding error that could affect calculations that require HIGH precision. Fortunately this is NOT your case. :slight_smile:

Here another link for a youtube video about floating point.

If you want to know more about the exercise itself, here is DemiPixel explaining the problem of the Exact Change in Medium.

Other users have also suggested solutions and streamed them, and you can see how they approached the float point problem you are facing. Check the following based on the Wiki for the Exact Change exercise

Hope this helps?

A professionally-employed programmer friend of mine once told me that javascript is generally considered a poor language for financial applications because all javascript numbers are floats. I’m not qualified to judge the worthiness of javascript in finance, but I do believe the 2nd half of his statement.

http://www.w3schools.com/js/js_numbers.asp

1 Like

Excellent point @belcurv! but then it is JS that it is asking to make them all float.

Absolutely.

The w3schools link also has examples of what @ecglover8 mentioned earlier in this thread: multiplying to “achieve” integers. Scroll down to the section “Precision”.

var x = 0.2 + 0.1;   // x will be 0.30000000000000004
var x = (0.2 * 10 + 0.1 * 10) / 10;   // x will be 0.3

Using precisionRound helped me through this.

I agree that rounding to the nearest integer can help the situation as well as multiplying like so.

var x = 0.2 + 0.1;   // x will be 0.30000000000000004
var x = (0.2 * 10 + 0.1 * 10) / 10;   // x will be 0.3

A few problems exist with these solutions.

  1. Firstly, rounding can easily create problems with numbers close to 0 e.g
Math.round(-0.48) // outputs -0

Obviously, you don’t want to use a -0 in your calculations.

  1. Secondly, if you multiply like the example above, you easily encounter a problem when you don’t know how many decimals your variables have.

Therefore, I believe the easiest way especially when dealing with unknown floats is to use a fixed number of decimal places to make the calculations. Of course, there is also an issue of being slightly off the number if your calculation goes on and on derivatively. But we can all agree that rounding to at least 4 decimal places is acceptable for most math calculations even in the professional world.

This solution worked for me

float_num.toFixed( x );

Where x is the number of decimal places required and float_num is you float.
This solution can be found at the link below:

Tell me what you think in the comments.

There have not been responses to this topic in 3 years.