I have a video and I need to get the seconds and milliseconds separated and the milliseconds rounded down to the nearest tenth. If the video duration starts off at 4.333333333333333 secs I use floor(num * 10) / 10 to get it to 4.3. When I try to get the remainder by itself using .truncatingRemainder(dividingBy: 1) and/or modf(num).1 I keep getting 0.2999999999999998 instead of 0.3. Why is this happening?
let mixCompositionDuration = CMTimeGetSeconds(mixComposition.duration)
print("mixCompositionDuration: ", mixCompositionDuration) // 4.333333333333333
let dubl = Double(mixCompositionDuration)
print("dubl: ", dubl) // 4.333333333333333
let mixCompositionRoundDown = floor(dubl * 10) / 10
print("mixCompositionRoundDown: ", mixCompositionRoundDown) // 4.3
let remainder = mixCompositionRoundDown.truncatingRemainder(dividingBy: 1)
print("remainder: ", remainder) // 0.2999999999999998
let mod = modf(mixCompositionRoundDown)
print("mod: ", mod) // (4.0, 0.2999999999999998)
let modRemainder = mod.1
print("modRemainder: ", modRemainder) // 0.2999999999999998
Modified Question.
My fitness app will calculate the number of calories burned based on a calculated value for each second. I have a timer that will allow the app to pick back up should it. I can't get the running sum to continue calculating when the app goes into the background. I tried to place the running sum inside of a DispatchQueue but not getting the sum as expected. Any guidance is greatly appreciated.
Here's the code I have placed inside the function that updates the timer.
//MARK: - Update Timer Label
func updateTimerLabel() {
interval = -Int(timerStartDate.timeIntervalSinceNow)
time = interval
let hours = interval / 3600
let minutes = interval / 60 % 60
let seconds = interval % 60
print("Current interval = \(interval)")
timerLabel.text = String(format:"%02i:%02i:%02i", hours, minutes, seconds)
DispatchQueue.global(qos: .background).async {
if self.activityArray[self.currentArrayRow].2 <= 4.5 {
self.cps = self.activityArray[self.currentArrayRow].2 * Double(self.user.userWeightInKilo) / 3600
self.runningCPS = self.runningCPS + self.cps
print("MET \(self.activityArray[self.currentArrayRow].2) <= 4.5 * KG (\(Double(self.user.userWeightInKilo))) * HR (\(Double(self.user.userHeartRate))) / MaxHR (\(Double(self.user.maxHeartRate)) * interval \(Double(self.interval)) / 3600. Gives a cps 0f \(self.cps) and a runningCPS of \(self.runningCPS) ")
} else {
self.cps = self.activityArray[self.currentArrayRow].2 * Double(self.user.userWeightInKilo) * Double(self.user.userHeartRate) / Double(self.user.maxHeartRate) / 3600
self.runningCPS = self.runningCPS + self.cps
print("MET \(self.activityArray[self.currentArrayRow].2) > 4.5 * KG (\(Double(self.user.userWeightInKilo))) * HR (\(Double(self.user.userHeartRate))) / MaxHR (\(Double(self.user.maxHeartRate)) * interval \(Double(self.interval)) / 3600. Gives a cps 0f \(self.cps) and a runningCPS of \(self.runningCPS) ")
}
}
activeLabel.text = String(format: "%0.1f", runningCPS) + " Calories Burned"
}
I have Hours / Minutes / Seconds that I would like converted into an integer
7 Hours 30 Minutes 5 Seconds
You could use split():
import Foundation
func getSecondsFromString(timeString: String) -> (Int) {
let timeParts = timeString.replacingOccurrences(of: "[^0-9]", with: " ", options: [.regularExpression])
.split(separator: " ")
.map{Int($0)!}
return timeParts[0] * 3600 + timeParts[1] * 60 + timeParts[2]
}
print(getSecondsFromString(timeString: "7 Hours 30 Minutes 5 Seconds"))
Output:
27005
If you have the time difference between two dates in seconds using timeIntervalSince then you can directly convert this into hours as a double by doing
let hours: Double = elapsedTime / 3600 // 7.501388...
In your examples this is 7.5 which you can then multiply with the hourly rate. If you for some reason only want to use full hours you can either round to the nearest full hour
let fullHours = round(hours) // 8.0
or if you want to truncate minutes and keep the hour then you can do a integer division from the start
let hours: Int = elapsedTime / 3600 // 7
I get the following error when using code for an extension, I'm not sure if they're asking to just use a different operator or modify the values in the expression based on an internet search.
Error: % is unavailable: Use truncatingRemainder instead
Extension code:
extension CMTime {
var durationText:String {
let totalSeconds = CMTimeGetSeconds(self)
let hours:Int = Int(totalSeconds / 3600)
let minutes:Int = Int(totalSeconds % 3600 / 60)
let seconds:Int = Int(totalSeconds % 60)
if hours > 0 {
return String(format: "%i:%02i:%02i", hours, minutes, seconds)
} else {
return String(format: "%02i:%02i", minutes, seconds)
}
}
}
The error(s) occur when setting the minutes and seconds variables.
CMTimeGetSeconds() returns a floating point number (Float64 aka
Double). In Swift 2 you could compute the
remainder of a floating point division as
let rem = 2.5 % 1.1
print(rem) // 0.3
In Swift 3 this is done with
let rem = 2.5.truncatingRemainder(dividingBy: 1.1)
print(rem) // 0.3
Applied to your code:
let totalSeconds = CMTimeGetSeconds(self)
let hours = Int(totalSeconds / 3600)
let minutes = Int((totalSeconds.truncatingRemainder(dividingBy: 3600)) / 60)
let seconds = Int(totalSeconds.truncatingRemainder(dividingBy: 60))
However, in this particular case it is easier to convert the duration
to an integer in the first place:
let totalSeconds = Int(CMTimeGetSeconds(self)) // Truncate to integer
// Or:
let totalSeconds = lrint(CMTimeGetSeconds(self)) // Round to nearest integer
Then the next lines simplify to
let hours = totalSeconds / 3600
let minutes = (totalSeconds % 3600) / 60
let seconds = totalSeconds % 60
The % modulus operator is defined only for integer types. For floating-point types, you need to be more specific about the kind of IEEE 754 division/remainder behavior you want, so you have to call a method: either remainder or truncatingRemainder. (If you're doing floating-point math you actually need to care about this, and lots of other stuff, or you can get unexpected / bad results.)
If you actually intend to do integer modulus, you need to convert the return value of CMTimeGetSeconds to an integer before using %. (Note that if you do, you'll lop off the fractional seconds... depending on where you're using CMTime that may be important. Do you want minutes:seconds:frames, for example?)
Depending on how you want to present CMTime values in your UI, it might be better to extract the seconds value and pass it to NSDateFormatter or NSDateComponentsFormatter so you get appropriate locale support.
Bring back the simple modulo syntax in swift 3:
This syntax was actually suggested on Apples official swift mailing list here but for some reason they opted for a less elegant syntax.
infix operator %%/*<--infix operator is required for custom infix char combos*/
/**
* Brings back simple modulo syntax (was removed in swift 3)
* Calculates the remainder of expression1 divided by expression2
* The sign of the modulo result matches the sign of the dividend (the first number). For example, -4 % 3 and -4 % -3 both evaluate to -1
* EXAMPLE:
* print(12 %% 5) // 2
* print(4.3 %% 2.1) // 0.0999999999999996
* print(4 %% 4) // 0
* NOTE: The first print returns 2, rather than 12/5 or 2.4, because the modulo (%) operator returns only the remainder. The second trace returns 0.0999999999999996 instead of the expected 0.1 because of the limitations of floating-point accuracy in binary computing.
* NOTE: Int's can still use single %
* NOTE: there is also .remainder which supports returning negatives as oppose to truncatingRemainder (aka the old %) which returns only positive.
*/
public func %% (left:CGFloat, right:CGFloat) -> CGFloat {
return left.truncatingRemainder(dividingBy: right)
}
This simple swift 3 migration tip is part of a more comprehensive swift 3 migration guide with many insights (35k loc / 8-days of migration) http://eon.codes/blog/2017/01/12/swift-3-migration/
There's no need to create a separate modulo operator for floating point numbers, unless you think it makes the code safer. You can overload the % operator to accept floating point numbers like so:
func %<N: BinaryFloatingPoint>(lhs: N, rhs: N) -> N {
lhs.truncatingRemainder(dividingBy: rhs)
}
Usage
let a: Float80 = 10
let b: Float80 = 3
print(a % b)
You can now use % with any two floating point numbers of the same tye.
I found that the following works in Swift 3:
let minutes = Int(floor(totalSeconds / 60))
let seconds = Int(totalSeconds) % 60
where totalSeconds is a TimeInterval (Double).
My Timer is displaying Minutes and Hours, but once it gets to 60 minutes it restarts from 0 Minute.
Should I get rid of the modulo ( % 60 ) for minutes.
I would like my timer to display for ex: 80:45 ( basically not stopping at 60 min once it reaches 1 hour)
var min = 0
var sec = 0
func stringFromTimeInterval(interval: NSTimeInterval) -> String {
let interval = Int(interval)
let seconds = interval % 60
let minutes = (interval / 60) % 60
//let hours = (interval / 3600)// I don't need the hours
return String(format: "%02d:%02d",minutes, seconds)
}
% 60 means that it will spit out a minutes value that is the remainder when divided by 60(minutes). This is most probably because for time in the form hh:mm, you want it to go from 5:59 to 6:00, not 5:60. So changing the following line will give you what you seek.
let minutes = (interval / 60) % 60 -> let minutes = interval / 60