-1

This question is related to an earlier question however I am receiving an infinite number not related to a divided by 0 problem. For example, the code below prints 4.5300000000000002 in the console but is flagged as .isInfinate and therefore I cannot store using Codable. How can I derive 4.53 (as a double) from this example?

  //Calculation  
     func calculateMaximumAndAverageSkatingEfficiency() {

            let heartRateUnit:HKUnit = HKUnit(from: "count/min")
            let heartRatesAsDouble = heartRateValues.map { $0.quantity.doubleValue(for: heartRateUnit)}
            let maxHeartRate = heartRatesAsDouble.max()
            guard let maxHeartRateUnwrapped = maxHeartRate else { return }

            maximumEfficiencyFactor = ((1760.0 * (maxSpeed / 60)) / maxHeartRateUnwrapped).round(to: 2)

            guard let averageIceTimeHeartRateUnwrapped = averageIceTimeHeartRate else { return }

            averageEfficiencyFactor = ((1760.0 * (averageSpeed / 60)) / averageIceTimeHeartRateUnwrapped).round(to: 2)

        }

    //Round extension I am using
    extension Double {

        func round(to places: Int) -> Double {
            let divisor = pow(10.0, Double(places))
            return Darwin.round(self * divisor) / divisor
        }

    }

//Usage 
  if let averageEfficiencyFactorUnwrapped = averageEfficiencyFactor {
        if averageEfficiencyFactorUnwrapped.isFinite {
            hockeyTrackerMetadata.averageEfficiencyFactor = averageEfficiencyFactorUnwrapped.round(to: 2)
        } else {
            print("AEF is infinite")
        }

    }
GarySabo
  • 5,806
  • 5
  • 49
  • 124
  • 3
    The number `4.5300000000000002` is not infinite at all. Did you get the number when you print the `Double`? Did you check that it is infinite with `theNumber.isInfinite` – Ryan Feb 23 '18 at 20:53
  • Yes that's the number that prints in the console then gets flagged via `.isInfinite` – GarySabo Feb 23 '18 at 20:55
  • 2
    A [mcve] would be helpful. – Martin R Feb 23 '18 at 20:57
  • @MartinR sorry I linked my earlier question because the code is the same – GarySabo Feb 23 '18 at 20:59
  • 1
    The code you posted doesn't mention `isInfinite`. Please post the code that actually demonstrates the problem. The error message you quoted in your other question says the problem is with `maximumCapableSpeed`, but your code doesn't mention that property either. – rob mayoff Feb 23 '18 at 21:14
  • @robmayoff edited, thank you! – GarySabo Feb 23 '18 at 21:16
  • 1
    Are you aware that, in the code you posted, if `averageEfficiencyFactorUnwrapped` is infinite, you try to round it, but if `averageEfficiencyFactorUnwrapped` is **not** infinite, you erroneously print that it **is** infinite? – rob mayoff Feb 23 '18 at 21:17
  • You probably want to use `averageEfficiencyFactorUnwrapped.isFinite` instead, as it will exclude both infinities and NaNs. – rob mayoff Feb 23 '18 at 21:19
  • Agreed with @CodeBender that specially answer https://stackoverflow.com/a/38036978/97337 has lots of options. – Rob Napier Feb 23 '18 at 21:25
  • Thanks all, Rob's answer is still needed as far as rounding but i had mistakenly used `isInfinite` instead of `isFinite` which also contributed to my problem so thank you for pointing out as well. – GarySabo Feb 23 '18 at 21:50

1 Answers1

4

Double cannot precisely store 4.53 for the same reason that you cannot precisely write down the value 1/3 in decimal. (See What Every Programming Should Know About Floating-Point Arithmetic if this is unclear to you.)

If you want your rounding to be in decimal rather than binary, then you need to use a decimal type. See Decimal.

Your round(to:) method is incorrect because it assumes "places" are decimal digits. But Double works in binary digits. I believe what you want is this:

extension Double {
    func round(toDecimalPlaces places: Int) -> Decimal {
        var decimalValue = Decimal(self)
        var result = decimalValue
        NSDecimalRound(&result, &decimalValue, places, .plain)
        return result
    }
}

Note that 4.53 is in no way "infinite." It is somewhat less than 5. I don't see anywhere in your code that it should generate an infinite value. I would double-check how you're determining that.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • I still need to end up with a Double however, would `let someDouble = Double(someDecimal as NSNumber)` be the best way to do that? – GarySabo Feb 23 '18 at 23:05
  • It is not possible to store 4.53 in a Double exactly. If you convert back to Double, you will get binary (rather than decimal) rounding to a value close to (but not exactly) 4.53. – Rob Napier Feb 24 '18 at 13:56