2

I receive a float number from the user and check if it's valid depending on an increment defined (0.1, 0.5, 0.01, etc). For example, if the increment is 0.5, then 1.0, 1.5, 2.0 are valid but 1.2 is not. I'm using modulo to check if it is valid.

if (input % increment) == 0 {
    println("Pass")
} else {
    println("Fail")
}

The problem is that when the increment is 0.1, most of the valid values of input are detected as invalid.

I used the formula given in this answer and it improves the detection of valid inputs but still fails most of the time.

Community
  • 1
  • 1
Juan
  • 404
  • 3
  • 13

4 Answers4

4

Your problem is that 0.1 isn't exactly representable in binary whereas 0.5 for instance is. this means that 0.1 * 5 doesn't equal 0.5. It's a lot like in decimal where (1 / 3) * 3 ≃ 0.3333333 * 3 = 0.99999999 not 1. To solve this sort of problem you can introduce an epsilon. An epsilon is a very small value, and you check whether your result is only a very small distance from the value you actually want, if it is you hope that the value is correct.

James Snook
  • 4,063
  • 2
  • 18
  • 30
4

Based on James Snook's answer, I ended up with this solution.

let epsilon = 1e-6
let division = input / increment
let diff = abs(division - round(division))
if (diff < epsilon) {
    println("Pass")
} else {
    println("Fail")
}
Community
  • 1
  • 1
Juan
  • 404
  • 3
  • 13
3

To handle numbers with one number after the decimal point you could do:

if (input * 10) % (increment * 10) == 0 {
    println("Pass")
} else {
    println("Fail")
}

To handle numbers with more than one number after the decimal point just increase the multiple. For example, if (input * 100000) % (increment * 100000) == 0.

Daniel Storm
  • 18,301
  • 9
  • 84
  • 152
  • That's a good idea but it doesn't work with increments like 0.5 or 0.25, the main issue is that I receive the increment from an API, right now I need to handle max 2 decimal (0.01) but in the future they could send me 0.001 or more. – Juan May 25 '15 at 19:03
  • @Juan how does this not work with `0.5` exactly? You could change this to multiply by `100` to handle two decimal places and `1000` for three decimal places, etc... – Daniel Storm May 25 '15 at 19:10
3

I believe that the modulo works only with integers, if I'm right you should do your own workaround to see if the given number is a multiple or not, which translate to: the result of the division must be an integer. The code should be something like this:

let floatDivision = input/increment;
let isInteger = floor(floatDivision) == floatDivision // true
if (isInteger) {
    println("Pass")
} else {
    println("Fail")
}

And this should work with any increment number (even more than one decimal point digit).

EDIT

as James Said, the float division of 1.2 over 0.1 is not coded exactly as 12 but 11.9999... So I added the epsilon in the comparison:

let input = 1.2; 
let increment = 0.1; 
let epsilon = 0.00000000000001;

let floatDivision = input/increment;
let dif = abs(round(floatDivision) - floatDivision);

println(String(format: "%.20f", floatDivision)); // 11.99999999999999822364  
println(String(format: "%.20f", round(floatDivision))); // 12.00000000000000000000  
println(String(format: "%.20f", dif)) // 0.00000000000000177636

let isInteger = dif < epsilon // Pass if (isInteger) {
    println("Pass") } else {
    println("Fail") }

Best of luck.

MAB
  • 953
  • 7
  • 14
  • 1
    [Unlike the remainder operator in C and Objective-C, Swift’s remainder operator can also operate on floating-point numbers](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html) – Daniel Storm May 25 '15 at 17:23
  • Thx didn't know that :) So Juan code should work unless what's happening behind the scene is like what James described. – MAB May 25 '15 at 17:39
  • Exactly. Jame's answer is the explanation for why the OP is experiencing this problem. – Daniel Storm May 25 '15 at 17:42
  • 1
    So no need to use the the remainder operator. The division is working fine. I tried it with an increment as small as 0.0000000001 – MAB May 25 '15 at 17:49
  • I think that the increment isn't the problem or at least not the only one, if you use as input 1.2 then it's going to fail but it shouldn't – Juan May 25 '15 at 18:50
  • You're right, oddly it worked with some combinations but not others (for example 1.3/0.1 but not 1.2/0.1) I updated the answer to ignore small differences as described by James. – MAB May 25 '15 at 19:58