1

I have code that examines if a variable elapsedTime (double) is a multiple of 0.4. i.e. 0.4, .0.8, 1.2, 1.6 etc code checks like so:

    let roundedInterval = Double(round(timeInterval*10)/10) //(this is 0.4)
    let roundedActualElapsedTime = Double(round(actualElapsedTime*10)/10)

        if roundedActualElapsedTime%roundedInterval == 0 {

             print(“is a multiple of 0.4”)
    }

However from my results output 0.4, 0.8, 1.6, 3.2 are being reported as multiples, so it is missing values. Any ideas what I might be doing wrong here?

NOTE: roundedTimeInterval remains constant at 0.4 and roundedActualElapsedTime increments in 0.1s i.e. 0.1, 0.2, 0.3, 0.4... etc

KexAri
  • 3,867
  • 6
  • 40
  • 80
  • what's your timeInterval and actualElapsedTime for example? – aaisataev Jan 13 '16 at 06:15
  • @aaisataev edited the question – KexAri Jan 13 '16 at 06:19
  • See [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken): `0.4` *cannot* be represented exactly as a binary floating point number. – Martin R Jan 13 '16 at 06:25
  • 1
    [What Every Computer Scientist Should Know About Floating-Point Arithmetic](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) – Martin R Jan 13 '16 at 06:30

1 Answers1

5

The % operator for Float and Double is probably going to be removed in Swift 3.0, so you should not rely on it. See this swift-evolution thread for the discussion.

Furthermore, you need to learn more about floating point numbers. Floating point numbers (as implemented on all of Apple's platforms) can only represent numbers of the form m * 2e, where m and e are integers (and may be negative). Only integer multiples of a power of two (including negative powers like 1/2, 1/4, 1/8, etc.) can be represented exactly, and numbers like 0.1, 0.2, and 0.3 are not integer multiples of any power of two. (Note that 0.5 is an integer multiple of a power of two: it's 2-1.)

Thus when you take round(timeInterval * 10) and divide it by 10, you generally get an inexact result, and in general you shouldn't expect the remainder of two inexact results to be exactly zero.

What you probably want to do is work with deciseconds (tenths of seconds) as integers, since then you're dealing with exact numbers:

let roundedTenths = Int(round(timeInterval*10))
let roundedElapsedTenths = Int(round(actualElapsedTime * 10))
if roundedElapsedTenths % roundedTenths == 0 {
    print("is a multiple of 0.4")
}
rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • The powers of 2 would explain why 0.5 was working as a time interval and 0.4 wasn't then. Wow mad props to you for knowing that. Fixed now. If I wanted to increase the accuracy I could use *100 instead of *10 right? – KexAri Jan 13 '16 at 06:41
  • Yes, you could multiply both numbers by 100. – rob mayoff Jan 13 '16 at 07:08