Why 0.1 + 0.05 is not equal to 0.15

Those blocks end up with NOO msg. Why?

You just discovered the wonderful world of floating point data types.

The value 0.1 internal representation is an infinitely repeating number, just like 1/3 gives you an infinitely long string in decimal.

So don't expect exact equality.

Have you considered switching to Cobol?

There is probably a bunch of old programs in Cobol in government and banking data centers, whose authors have retired or aged out.

No comment

How to deal with this:

More sample exact math blocks:

In our software ( for woodworking machines ) to compare floating numbers we use a function EqualWithinMargin to which we pass the allowable error ( could also be called InRange )

1 Like

Or you can use compare texts = blocks

2 Likes

A couple of other methods:

Now good answers. Thanks all.

To throw a potential wrinkle into this discussion (but an interesting wrinkle!), App Inventor blocks map to the Scheme programming language under the hood. Scheme supports two different numerical systems: exact numbers and inexact numbers.

Inexact numbers are effectively the IEEE 754 floating point numbers, which are expressed in base 2. Because .1 and .05 are not expressible as the sum of a set of powers of 2, the underlying expression in binary is inexact vs the original decimal value and you get a slight rounding error due to this that shows up in the math.

Exact numbers, on the other hand, can express any rational number (within limits of the machine memory, etc.). In this system, numbers are expressed as two separate values, numerator and denominator, and operations on them take this into account (you may know them as fractions). 0.10 can be expressed as 1/10, both of which are expressible as sums of powers of 2 (1 = 20 and 10 = 23 + 21). 0.05 similarly is 1/20 and 20 = 24 + 22.

Numbers expressed with a decimal part are always considered inexact. You can build exact numbers and do some operations on them. For the original example, you can do:

blocks(59)

and the resulting value is really an exact number for other computations. However, when the number is turned into a string it becomes inexact before being formatted as a decimal number. In the actual scheme though, it retains its internal structure until that point:

#|kawa:3|# (+ .1 .05)
0.15000000000000002
#|kawa:4|# (+ (/ 1 10) (/ 1 20))
3/20
#|kawa:5|# (exact->inexact (+ (/ 1 10) (/ 1 20)))
0.15

This also has the interesting property of allowing for numbers that as decimals would have an infinitely repeating sequence (e.g., 1/3) while still being able to do the math over them. For example, we can add the numbers 1/3 and 1/6 to get 1/2:

or add 1/3 to itself three times:

If we combine an exact number with either an exact or inexact number we can see in the Scheme how the resulting expression changes:

#|kawa:6|# (+ (/ 1 3) .5)
0.8333333333333333
#|kawa:7|# (+ (/ 1 3) (/ 1 2))
5/6

Now, for most calculations 5/6 and 0.8333333333 aren't different but they are not technically equal. I've submitted a feature request to add a block to provide built-in semantics for comparing numbers within an error range to account for the minor discrepancies that happen when working with inexact numbers.

3 Likes

(added to FAQ)

idk why it does this, but try adding a 0 at the end of 0.1 to make it 0.10

No, even with 0.10, nothing changes. However, the subsequent answers clearly clarify the problem and provide the right solutions.

1 Like

oh ok

Your homework assignment:

https://www.google.com/search?q=floating+point

We have a PR that will solve this problem.
But I have a question to our community. Should we have a seperate block that has the tolerance property? or should we rather add a mutator on the original compare block that can take on a tolerance number?

I personally like the mutator approach better since we already have too much blocks on our Math section but what do people think?

1 Like

Personally, I believe that 0.1 + 0.2 should = 0.3, and 0.1 +0.05 should = 0.15 without having to handle the floating point types internal representations.

so some default tolerance should be already activated and a mutator where the user can tinker around the tolerance?

1 Like

My +1 for the mutator ( should also be included in comparisons >, >=, <, <= )

off topic
since we have discussed math blocks here, i have a request to change the default value of the first block in math drawer from 0 to 1. since block 1 is much more frequently used than block 0.

3 Likes

You have to be careful to avoid cases where two numbers are both equal to each other but also one is considered greater or lesser than the other.

That would require consistent leeway across comparison blocks.