0

I am trying to understand why swift is messing with the precision of a float when doing addition.

Here is a simple example of starting with 127.1 and adding 0.1 to it ..

var value: Float = 127.1

for number in 0...5  { 
    print(value)
    value += 0.1
}

and this is the output I get ..

127.1
127.2
127.299995
127.399994
127.49999
127.59999

... when what I was expecting is this:

127.1
127.2
127.3
127.4
127.5
127.6

Can someone explain why this happens and what if anything I can do to maintain the single decimal precision I need?

NOTE: I know I can format it as a string with with the precision I need, but that doesn't help me when I need to perform math and I can't rely on the precision of the number.

UPDATE: I did find an interesting article that talks about how computers deal with floating point math and is somewhat of an answer to "why" it happens but I'm curious what others think is the best way to get the result I need.

UPDATE: The best solution I've found is to convert to an Integer to do the math and then convert back to a Float ...

var value: Float = 127.1


for number in 0...9  { 
    print(value)
    var valueAsInt: Int = Int(value * 10)
    valueAsInt += 1
    value = Float(valueAsInt)/10
}
ra9r
  • 4,528
  • 4
  • 42
  • 52
  • 1
    Alternatively, you could try using `https://developer.apple.com/documentation/foundation/nsdecimalnumber` – Stephen C Oct 02 '22 at 04:12
  • 2
    You should use Swift native `Decimal` type. Make sure to do NOT USE a floating point literal. You can use its string initializer or [`init(sign: FloatingPointSign, exponent: Int, significand: Decimal)`](https://developer.apple.com/documentation/foundation/decimal/2143030-init) – Leo Dabus Oct 02 '22 at 05:00
  • You are using numbers and doing math with a one decimal precision so you should round and print with one decimal precision. – Joakim Danielson Oct 02 '22 at 09:44

1 Answers1

2

This is caused by Swift’s default formatting of floating-point numbers.

The actual values calculated in each iteration are:

127.09999847412109375
127.1999969482421875
127.29999542236328125
127.399993896484375
127.49999237060546875
127.5999908447265625

After the second number, these differ from the Float values closest to 127.1, 127.2, 127.3, 127.4, 127.5, and 127.6 due to accumulated rounding errors. The values of the Float type that are closest to those numbers are:

127.09999847412109375
127.1999969482421875
127.3000030517578125
127.40000152587890625
127.5
127.59999847412109375

As you can see, where these are the same, for 127.1 and 127.2, Swift has shown you “127.1” and “127.2”. For the other cases, Swift shows you more digits, which distinguishes the computed value from the corresponding value in the sequence 127.1, 127.2, 127.3, 127.4, 127.5, and 127.6.

I suspect Swift’s default formatting produces just enough digits to uniquely distinguish the true Float value. In other words, it shows enough digits that converting the shown digits back to Float would reproduce the Float value. In cases after the second, producing only a single digit after the decimal point would not accomplish this, so Swift produces more digits.

If you want to execute a loop with a value set to the Float values closest to 127.1, 127.2, 127.3, 127.4, 127.5, and 127.6, simply iterate an integer value (it can be the Float type) through 1271 to 1276 and set value to that divided by ten. With this technique, in each iteration, value will be affected by only one rounding error, in the division, so it will have the Float value closest to the desired number. There will be no accumulation of error through the iterations.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Thank you for taking the time to explain floating point but I had gotten that already from the article I linked to in the OP. What I was specifically looking to know was how to deal with it in Swift when I need to maintain the precision. – ra9r Oct 03 '22 at 05:46
  • @ra9r: As the last paragraph of the answer tells you, iterate with integers and divide the iterator by ten to get the nominal value for that iteration. – Eric Postpischil Oct 03 '22 at 05:47