3

I have a very simple function to convert temperature from ˚C TO ˚K.

func convertKelvinToCelsius(temp:Double) ->Double {
        return temp - 273.15
}

And I have a unit test to drive this function. This is where the problem is:

  func testKelvinToCelsius(){
            var check1 = conv.convertKelvinToCelsius(200.00) // -73.149999999999977
            var check2 = 200.00 - 273.15                     // -73.149999999999977
            var check3 = Double(-73.15)                      // -73.150000000000006

            //Passes
            XCTAssert(conv.convertKelvinToCelsius(200.00).description == Double(-73.15).description, "Shoud convert from celsius kelvin")

            //Fails
            XCTAssert(conv.convertKelvinToCelsius(200.00) == Double(-73.15), "Shoud convert from celsius kelvin")
     }

When you add a breakpoint and check the values of check1, check2 and check3, they are very interesting:

check1  Double  -73.149999999999977
check2  Double  -73.149999999999977
check3  Double  -73.150000000000006

Questions:

  1. Why does Swift return different values for check1/check2 and check3

  2. How can I get the second test to pass, because writing it like I did the test1 smells. Why should I have to convert Doubles to Strings to be able to compare them?

  3. Finally, when I println check1, check2 and check3, they all print to be '-73.15'. Why? Why not print accurately, and not confuse the programmers!?

To Reproduce:

Just type 200 - 273.15 == -73.15 in you playground and watch it go false!!

Shaunak
  • 17,377
  • 5
  • 53
  • 84
  • [What Every Computer Scientist Should Know About Floating-Point Arithmetic](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). – Martin R Mar 09 '15 at 17:24
  • Possible duplicate of [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken). – Even if that question was originally about JavaScript, the issue is the same in C or Swift (and many more languages). – Martin R Mar 09 '15 at 17:26

3 Answers3

5

This is expected behavior for floating point values. They cannot be 100% accurately represented.

You can use the XCTAssertEqualWithAccuracy function to assert floating point values are within a given range of each other.

The reason println prints the same value for all is because it internally rounds them to two decimals (I assume).

Rengers
  • 14,911
  • 1
  • 36
  • 54
  • sorry took back accept. My test passes, but I am still trying to understand why check 2 and check 3 would evaluate to slightly different values. – Shaunak Mar 09 '15 at 17:14
  • Again, this has to do with the fact that those numbers cannot be represented exactly internally. The numbers `200.00` and `237.15` are not those exact numbers, you will probably see this if you inspect those as separate variables. The small differences here add up and thus check2 and check3 are not equal. – Rengers Mar 09 '15 at 17:19
  • @Rengers: `200.00` *is* represented exactly in a `Double` (but `237.15` is not). So your initial sentence in the answer is slightly misleading. *Not all* real numbers can be represented accurately. – Martin R Mar 09 '15 at 17:23
2

This is not a Swift specific issue, this is related to the fact how decimal numbers are created in computers and what is their precision. You will need to work with DBL_EPSILON.

MirekE
  • 11,515
  • 5
  • 35
  • 28
1

Swift, like most languages, uses binary floating point numbers.

With binary floating point numbers, some numbers can be represented exactly, but most can't. What can be represented exactly are integers unless they are very large (for example, 100000000000000.0 is fine), and such integers multiplied or divided by powers of two (7.375 is fine, it is 59.0 / 8, but 7.3 isn't).

Every floating point operation gives you the exact result, rounded to the nearest floating-point number. So you get

200.0 -> Exactly 200
273.15 -> A number very close to 273.15
200 - 273.15 -> A number very close to -73.15
-73.15 -> A number very close to -73.15

If you compare two numbers that are both very very close to -73.15 they are not necessarily equal. That's not a problem of the == operator; that one will determine correctly whether they are equal or not. The problem is that the two numbers can actually be different.

gnasher729
  • 51,477
  • 5
  • 75
  • 98