1

I have tested while loop below and don’t understand the result.

var x: Float = 0.0
var counter = 0

 while x < 1.41
 {
     x += 0.1
     counter += 1

 }

 print (counter) // 15
 print (x) // 1.5

How is it possible to have the result x = 1.5 for the used while condition where x < 14.1 ? How to explain this result?

Update: and one more. Why the results are different for Double and Float ?

var x: Double = -0.5
var counter = 0

while x < 1.0
{
    x += 0.1
    counter += 1   
}

print (counter) // 16
print (x)//1.1

var x: Float = -0.5
var counter = 0

while x < 1.0
{
    x += 0.1
    counter += 1   
}

print (counter) // 15
print (x)//1.0

Update 2 and another one. Why there is no difference for < and <= conditions. Does it mean that usage of <= has no sense for floating point ?

var x: Double = 0.0
var counter = 0

while x < 1.5
{
    x += 0.1
    counter += 1
}

print (counter) // 15
print (x) //1.5


var x: Double = 0.0
var counter = 0

while x <= 1.5
{
    x += 0.1
   counter += 1
}

print (counter) // 15
print (x) //1.5
VYT
  • 1,071
  • 19
  • 35
  • What value do you expect `x` to have at the start of the last iteration of the `while` loop? And what value will it have at the end of that final iteration? – Mark Dickinson Nov 20 '16 at 21:50
  • @Hamish Ok, clear. I have added an update – VYT Nov 20 '16 at 21:59
  • Please don't combine several different questions in one; make a new question post instead! – Mark Dickinson Nov 20 '16 at 22:49
  • Possible duplicate of [For loop with float as counter, increasing by 0.01, does not print expected float values](https://stackoverflow.com/questions/16124040/for-loop-with-float-as-counter-increasing-by-0-01-does-not-print-expected-floa) – phuclv Apr 04 '19 at 14:27
  • [For loop is not running for float values](https://stackoverflow.com/q/8036368/995714), [Floating point inaccuracies](https://stackoverflow.com/q/4575643/995714) – phuclv Apr 04 '19 at 14:28

1 Answers1

3

What else would you expect? The loop is executed 15 times. On the 14th time, x is 1.4 and so you add another 0.1, making it 1.5.

If you expect the loop to terminate at 1.4, you should increment x before checking the while condition, not after that.

If you expect the loop to terminate on 1.41, your increment is wrong and you should do

x += 0.01

instead, making it 141 iterations.

As for the second question, I am aware that Float should not be used for monetary calculations and such due to its lack of precision. However, I trusted Double so far, and the while loop in run 15 actually claims the Double value to be less than 1.0 while it is reported to be 1.0. We have got a precision problem here, as we can see if we substract x from 1.0:

print(1.0-x)

which returns: 1.11022302462516e-16

At the same time, Float seems to be unprecise in the other direction. In the last run, it is a little bigger than 0.9 (0.9 + 5.96046e-08), making it bigger than 10 in the following run.

The reason why Double and Float are wrong in different directions is just a matter of how the values are stored, and the result will be different depending on the number. For example, with 2.0 both actual values are bigger: Double by 4.440892..e-16 and Float by 2.38419e-07. For 3.0 Double is bigger by 1.33226e-15 and Float smaller by 7.1525e-07.

The same problems occur using x.isLess(than: 1.0), but this method is the basis for the < operator as of https://developer.apple.com/reference/swift/floatingpoint/1849403-isless

isLessThanOrEqualTo(1.0), on the other hand, seems to work reliably as expected.

This answer is pretty much a question itself by now, so I'm curious if anyone has an in-depth explanation of this...

Update

The more I think about it, the less of a Swift problem it is. Basically, you have that problem in all floating point calculations, because they are never precise. Both Float and Double are not precise, Double is just twice as accurate. However, this means that comparisons like == are useless with floating point values unless they are both rounded. Therefore, good advice in loops like those of yours with a known precision (in your case one decimal) would be to round to that precision before doing any kind of comparison. For example, this would fix the loop:

var x: Double = -0.5
var counter = 0

while (round(x * 1000) / 1000) < 1.0
{
    x += 0.1
    counter += 1   
}

print (counter) // 15
print (x)//1.0

var x: Float = -0.5
var counter = 0

while (round(x * 1000) / 1000) < 1.0
{
    x += 0.1
    counter += 1   
}

print (counter) // 15
print (x)//1.0
Lupinity Labs
  • 2,099
  • 1
  • 15
  • 23
  • 1
    This is where the [**unit of least precision**](https://en.wikipedia.org/wiki/Unit_in_the_last_place) comes in. https://developer.apple.com/reference/swift/float/1846050-ulp – Alexander Nov 20 '16 at 22:59
  • @Lupinity Yes, agree, to round the values is a good solution ! – VYT Nov 20 '16 at 23:41