As part of your new program, you need to 'count' money from £0.0 to £1.00 in steps of 10p, but you notice that your computer generates some odd results. So you compile a table of the actual results versus the expected results, and the output is as follows:
Expected Result | Actual Result | Pass/Fail |
£0.0 | £0.0 | Pass |
£0.1 | £0.1 | Pass |
£0.2 | £0.2 | Pass |
£0.3 | £0.30000000000000004 | Fail |
£0.4 | £0.4 | Pass |
£0.5 | £0.5 | Pass |
£0.6 | £0.6 | Pass |
£0.7 | £0.7 | Pass |
£0.8 | £0.7999999999999999 | Fail |
£0.9 | £0.8999999999999999 | Fail |
£1.0 | £0.9999999999999999 | Fail |
Why is the program's answer different from what you might expect--surely adding 10p is simple?
This section requires Javascript.
You are seeing this because something didn't load right. We suggest you, (a) try
refreshing the page, (b) enabling javascript if it is disabled on your browser and,
finally, (c)
loading the
non-javascript version of this page
. We're sorry about the hassle.
Oh, now that is brilliant. So, could you shed some light on how floating point arithmetic works, from a numerical perspective, and possibly architectural perspective?
Log in to reply
On most computers, floating points values are implemenetd to the IEEE 754 Standard .
The linked article goes into a lot of detail about how the numbers are stored (and there are separate and detailed algorithms implemented as to how to display those numbers as a human readable value).
The crux of the issue is that all values are represted in binary, and in binary fractions many fractional values cannot be accurately represented in a finite number of binary digits.
You know that in binary, an integer is represented as a sequence of 1s & 0s, where the 1s & 0s indicate which powers of 2 are summed together :
5 = 1 0 1 2 = 1 × 2 2 + 0 × 2 1 + 1 × 2 0 = 4 + 1
For fractions we still have 1s & 0s, where they now indicate which powers of 2 1 are summed together :
4 3 = 0 . 1 1 = 1 × 2 1 1 + 1 × 2 1 2 = 2 1 + 4 1
You can see from this that some values are difficult to represent as a binary fraction - and some are impossible in a fixed number of binary digits :
1 0 1 = 0 . 0 0 0 1 1 0 0 1 1 0 0 1 1 . . . = 1 6 1 + 3 2 1 + 2 5 6 1 + 5 1 2 1 + 4 0 9 6 1 + 8 1 9 2 1 . . . (this sum continues indefinitely as far as I know).
Architecturally speaking, the floating point format was designed to optimise the balance between memory usage (for storage), computation costs, and the accuracy of the value which is represented. Computations are relatively easy in this format (as they are the same as normal binary addition for integers, which Processors are already designed for).
There is an alterantive format which has been implemented on some procesors and some langauges - which is effectively binary coded decimal fractions - where instead of the value being stored in binary, each decimal digit from 0 to 9 is stored in a sequence of 4 bits. The issue with this format is that although 4 bits are used to represent 10 possible values, the same bit string could actually represent 16 different values , and therefore the format is not space efficient. It is also complex for a computer to execute computations on such values, and often the processors had specific sections of the CPU designed to execute Binary coded decimal formats. It is worth nothing that even this format can't represent all values accurately - for instance just like using decimal fractions you cannot represent 3 1 accurately - the decimal 0.3333333 continues indefinitely.
I hope this helps,
Log in to reply
Ah, thanks a lot for the extra information, Tony. :) I appreciate it.
Imo, any solution/explanation about floating point arithmetic is incomplete without a link to the following paper:
What every computer scientist should know about floating-point arithmetic
Problem Loading...
Note Loading...
Set Loading...
Whenever you see this sort of output it is almost always the result of a bug in your application, specifically you have used a floating point type to represent your currency values, and floating point numbers cannot represent all values accurately.
The normal floating point number type cannot represent 0.1 (10p) or 0.01 (1p) accurately, and therefore calculations with them will result in accurate answers, especially when those calculations are repeated. To fix this issue you have 3 main options :
Perform all your calculations as integers in the lowest unit (in this case 1p), and then format the output correctly. Integer mathematics is always precise (unless you overflow your integer values), and you only have complexity in your input/output formatting (and even then your code will only be dividing by 100 or modulo 100.
Use a fixed precision number type. In many languages this will add complexity to your code, or more dependencies (as your application needs another library), both of which can lead to faults in your application.
Continue to use floating point, but make sure you format the output to round to 2 decimal places. While this may seem the quick fix it is nearly always the wrong answer, as you are still getting inaccurate results but hiding the issues behind your formatting.
It is recommended that you should always use Option 1 - as it is adds the least complexity and will always provide accurate calculations.
As proof of the inaccuracies of floating point numbers (in Python3, but C and other languages are likely to be similar) :