0

I'm building a cricket app. I want the number to increase by .1 but once it reaches .5 and I hit plus again, I want it to round up to the whole value.

eg. 2.1, 2.2, 2.3, 2.4, 2.5 then jumps to 3.0 (then starts over again 3.1, 3.2, 3.3 etc.)

My stepper currently goes up and down by .1 but it includes decimals (.6, .7, .8 & .9)

Everytime it hit's .5 of a number I need it to round up. Same when subtracting.

Here's my code:

var oversFloat: Float = 0.0

@IBOutlet weak var displayOversLabel: UILabel!
@IBAction func OversStepper(_ sender: UIStepper) {
    let oversValue = Float(sender.value)
    displayOversLabel.text = String(oversValue)

enter image description here

rmaddy
  • 314,917
  • 42
  • 532
  • 579
J.Kearney
  • 7
  • 1
  • 7
  • This is essentially a duplicate of [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken). – The real problem is that decimal fractions like 0.1, 0.2 are not represented exactly in a binary floating point variable. – Martin R Dec 16 '18 at 15:13

2 Answers2

4

Floating point numbers are troublesome. You should really never test a float point number for equality with another value. In your case 0.1 cannot be represented exactly with a floating point number, so increasing by 0.1 introduces increasing error. So your number might end in 0.499999999 when you were expecting 0.5.

In your case, you can easily avoid this issue by using whole numbers in your stepper. Modify your stepper to step by 1 instead of by 0.1 and multiply your minimum and maximum values by 10. Then, when you use your stepper value to update your label, divide the stepper value by 10.

For jumping from 88.5 to 89.0 which incrementing, and from 89.0 to 88.5 when decrementing, check for the one's digit being 6 or 9 and then increment/decrement your stepper value by 4:

@IBAction func oversStepper(_ sender: UIStepper) {
    let value = Int(sender.value)
    let remainder = value % 10
    if remainder == 6 {
        sender.value = Double(value + 4)
    } else if remainder == 9 {
        sender.value = Double(value - 4)
    }
    displayOversLabel.text = String(format: "%.1f", sender.value / 10)
}

By stepping with whole values, no error will be introduced.

vacawama
  • 150,663
  • 30
  • 266
  • 294
0

I add a general answer to your request in case the step number changes from 0.1 to 0.01 or 0.001.

The first one is to simplify the case so that the @IBAction can be called without confusing.

      @IBAction func oversStepper(_ sender: UIStepper) {
        let myValue : MyFloat = MyFloat(Float(sender.value))
        sender.value = myValue.value

        displayOversLabel.text = String(format: "%.1f", sender.value)
    }

MyFloat here plays a role as a number validator. the UIStepper value can be corrected by a MyFloat object.

The next question is Float can be compared by a precision control way. In other words, the accuracy of Float number can be achieved by limiting the abs of different between a number and an actual number, such as abs(a-b) < precision (1e-4), so we know they are close enough.

Based on this assumption, construct a MyFloat and run testCases as following:

    let presicion: Float = 1e-4

    struct MyFloat {
        private var val: Float = 0
        init(_ v: Float){value = v}
        var value : Float {
            get{ return val}
            set{
                 let ro = newValue.rounded()
                 let ground = newValue.rounded(FloatingPointRoundingRule.towardZero)
                 val = (trunc(newValue) == round(newValue) || abs((ground + ro) / 2.0 - newValue) < presicion ) ? newValue :
                 (abs(val - ro) < presicion) ? (ground + ro) / 2.0 : ro
            }
        }
        static   func +=(left: inout MyFloat, right: Float){
            left.value = left.value + right
        }
        static   func -=(left: inout MyFloat, right: Float){
            left.value = left.value - right
        }
    }


    //naive testCases
    var myFloat = MyFloat(10.0)
    myFloat += 0.1; assert(abs( myFloat.value - 10.1) < presicion);
    myFloat += 0.1; assert(abs( myFloat.value - 10.2) < presicion);
    myFloat += 0.1; assert(abs( myFloat.value - 10.3) < presicion);
    myFloat += 0.1; assert(abs( myFloat.value - 10.4) < presicion);
    myFloat += 0.1; assert(abs( myFloat.value - 10.5) < presicion);
    myFloat += 0.1; assert(abs( myFloat.value - 11.0) < presicion);
    myFloat += 0.1; assert(abs( myFloat.value - 11.1) < presicion);
    myFloat += 0.1; assert(abs( myFloat.value - 11.2) < presicion);
    myFloat += 0.1; assert(abs( myFloat.value - 11.3) < presicion);
    myFloat += 0.1; assert(abs( myFloat.value - 11.4) < presicion);
    myFloat += 0.1; assert(abs( myFloat.value - 11.5) < presicion);
    myFloat += 0.1; assert(abs( myFloat.value - 12.0) < presicion);
    myFloat += 0.1; assert(abs( myFloat.value - 12.1) < presicion);
    myFloat += 0.1; assert(abs( myFloat.value - 12.2) < presicion);
    myFloat -= 0.1; assert(abs( myFloat.value - 12.1) < presicion);
    myFloat -= 0.1; assert(abs( myFloat.value - 12.0) < presicion);
    myFloat -= 0.1; assert(abs( myFloat.value - 11.5) < presicion);
    myFloat -= 0.1; assert(abs( myFloat.value - 11.4) < presicion);
    myFloat -= 0.1; assert(abs( myFloat.value - 11.3) < presicion);
    myFloat -= 0.1; assert(abs( myFloat.value - 11.2) < presicion);
    myFloat -= 0.1; assert(abs( myFloat.value - 11.1) < presicion);
    myFloat -= 0.1; assert(abs( myFloat.value - 11.0) < presicion);
    myFloat -= 0.1; assert(abs( myFloat.value - 10.5) < presicion);
    myFloat -= 0.1; assert(abs( myFloat.value - 10.4) < presicion);
    myFloat -= 0.1; assert(abs( myFloat.value - 10.3) < presicion);
    myFloat -= 0.1; assert(abs( myFloat.value - 10.2) < presicion);

The advantage of this approach is the step value can be much more precise as like 0.001 without any new code, and only one parameter needs care: precision. (for example, precision = 1e-6 will give a wrong answer in this case.)

BTW, this answer also applies to negative values by masking the range of (-1 + n,-0.5 + n), n<=0.

E.Coms
  • 11,065
  • 2
  • 23
  • 35