Why does 16//1 and 16.0 not equal 16 in for loop?

I am getting some behavior I can not explain. The context is that a chamber gets 9/16’ths of its content emptied, initially air. Then the 9/16ths that were removed are replaced with some pure gas. This process is repeted, and I wanted to find out how fast one starts getting pure gas. So I wrote the following script, and included a sanity-check that the air part a plus the gas part g always sum to 16. But the boolean comparison failed in the following three versions of the script:
Normal division, comparison with 16 as Int:

julia> begin
           a = 16
           g = 0
           counter = 0
           for i in 1:2
               a *= 7/16
               g *= 7/16
               g += 9
               counter +=1
               if a+g !== 16
                   println("Error! $a+$g=$(a+g) does not equal 16.")
                   #error("Gas parts do not sum to 16")
               end
               println("Iteration $counter:\nGas to air ratio = $(round(g/a, sigdigits=3))\n\n")
           end
       end
Error! 7.0+9.0=16.0 does not equal 16.
Iteration 1:
Gas to air ratio = 1.29


Error! 3.0625+12.9375=16.0 does not equal 16.
Iteration 2:
Gas to air ratio = 4.22

Even though

julia> 0.005+15.995
16.0

julia> 0.005+15.995 == 16
true

Rational division, comparison with 16 as Int:

julia> begin
           a = 16
           g = 0
           counter = 0
           for i in 1:2
               a *= 7//16
               g *= 7//16
               g += 9
               counter +=1
               if a+g !== 16
                   println("Error! $a+$g=$(a+g) does not equal 16.")
                   #error("Gas parts do not sum to 16")
               end
               println("Iteration $counter:\nGas to air ratio = $(round(g/a, sigdigits=3))\n\n")
           end
       end
Error! 7//1+9//1=16//1 does not equal 16.
Iteration 1:
Gas to air ratio = 1.29


Error! 49//16+207//16=16//1 does not equal 16.
Iteration 2:
Gas to air ratio = 4.22

Even though

julia> 16//1==16
true

Rational division, comparison with 16 as float:

julia> begin
           a = 16
           g = 0
           counter = 0
           for i in 1:2
               a *= 7//16
               g *= 7//16
               g += 9
               counter +=1
               if a+g !== 16.0
                   println("Error! $a+$g=$(a+g) does not equal 16.")
                   #error("Gas parts do not sum to 16")
               end
               println("Iteration $counter:\nGas to air ratio = $(round(g/a, sigdigits=3))\n\n")
           end
       end
Error! 7//1+9//1=16//1 does not equal 16.
Iteration 1:
Gas to air ratio = 1.29


Error! 49//16+207//16=16//1 does not equal 16.
Iteration 2:
Gas to air ratio = 4.22

The only successful version was without rationals, and where 16 was defined as a float as opposed to an int:

julia> begin
           a = 16
           g = 0
           counter = 0
           for i in 1:2
               a *= 7/16
               g *= 7/16
               g += 9
               counter +=1
               if a+g !== 16.0
                   println("Error! $a+$g=$(a+g) does not equal 16.")
                   #error("Gas parts do not sum to 16")
               end
               println("Iteration $counter:\nGas to air ratio = $(round(g/a, sigdigits=3))\n\n")
           end
       end
Iteration 1:
Gas to air ratio = 1.29


Iteration 2:
Gas to air ratio = 4.22

I can not explain why they dont all evaluate to true. Can you help me understand?

1 Like

!== == !(===). It checks whether the two values are identical (i.e. inseparable by the computer). You’re looking for !=.

6 Likes

Oh snap you’re right! My bad. Thanks!

But it’s not just about != vs !==. You shouldn’t have conditions depend on floating point comparisons, since you will very frequently run into floating point inaccuracies.

4 Likes

What about using isapprox?

1 Like

Yes.

1 Like

Right. That was one of the things I was checking for by printing the sum. But as a general principle, you are right, isapprox is better suited. I just looked up the docstring and realized that the default tolerances for isapprox are actually set just right for comparison of floating point numbers. Thanks!

I felt like the difference between !== and != was the more pressing question, but yes, this is better.

is another way to write isapprox.

1 Like