osku
osku

Reputation: 315

Splitting and combining of Double in Swift

I want to store duration of time in a Double and need to extract high and low values of Double (hour and minute) for accessing them. I also need to be able to set Double with two Ints (hour and minute).
And I got something here now:

extension Double {
    func hour() -> Int {
        return NSNumber(double: floor(self)).integerValue
    }

    func minute() -> Int {
        return NSNumber(float: Float(Double((self - floor(self)) * 100))).integerValue
    }

    init(hour: Int, minute: Int) {
        self = Double(hour) + ( Double(minute) / 100 )
    }
}

And then I've performed some testing with this in my appDelegate's didFinishLaunchingWithOptions:

var time: Double = 18.30

NSLog("Time is \"%f\" - so hours is \"%d\" and minutes is \"%d\".", time, time.hour(), time.minute())

time = Double(hour: 10, minute: 1)
NSLog("Time is \"%f\" - so hours is \"%d\" and minutes is \"%d\".", time, time.hour(), time.minute())

time = Double(hour: 15, minute: 45)
NSLog("Time is \"%f\" - so hours is \"%d\" and minutes is \"%d\".", time, time.hour(), time.minute())

Now, this code works. One might wonder why I convert Double to Float in the minute() function. This is because of an unknown issue that I faced when using Doubles without converting them to Floats before converting these Floats to Ints. If minutes didn't end with 0, one minute was subtracted for unknown reasons.
For example, 10:42 returned 10 hours and 41 minutes.
But 10:30 was still 10 hours and 30 minutes.
Also 10:00 was 10 hours and 0 minutes.

This routine that I posted here seems to be working, but I was just wondering, if I am as efficient as possible... There's more conversion here than what would be needed.
So, any improvements?

And does someone know the reason for unknown issue with decremented minute I mentioned?

Upvotes: 1

Views: 475

Answers (1)

Martin R
Martin R

Reputation: 539975

A binary floating point number such as Double or Float cannot represent the number 10.42 exactly, therefore multiplying the fractional part by 100 and truncating the result might give 41. With the conversion to Float you are just "hiding" the problem.

Here is a version that rounds instead of truncating, and is overall a bit simpler. I have adopted the notation of "minute" from your code, even if it represents 1/100 of an hour and not 1/60.

extension Double {

    func hour() -> Int {
        return Int(self)
    }

    func minute() -> Int {
        return Int(round(self * 100.0)) % 100
    }

    init(hour: Int, minute: Int) {
        self = Double(hour) + ( Double(minute) / 100 )
    }
}

Other possible approaches to solve your problem:

  • Store the duration as an integer representing the total number of minutes. E.g. "10:42" would be stored as 10*60 + 42. Then you have no rounding problems at all.

  • Use the Foundation type NSDecimalNumber instead of Double, which can represent a decimal integer with up to 38 digits exactly.

Upvotes: 1

Related Questions