truncatingRemainder vs remainder in Swift
truncatingRemainder
computes the remainder of the "truncating
division", and remainder
computes the remainder of the "rounding division".
Example (from the API reference):
let x = 8.625
let y = 0.75
Truncating division and remainder:
let q1 = (x/y).rounded(.towardZero)
let r1 = x.truncatingRemainder(dividingBy: y)
print(q1, r1) // 11.0 0.375
print(q1 * y + r1) // 8.625
Rounding division and remainder:
let q2 = (x/y).rounded(.toNearestOrEven)
let r2 = x.remainder(dividingBy: y)
print(q2, r2) // 12.0 -0.375
print(q2 * y + r2) // 8.625
So in any case, the remainder rem
of a division x
by y
is
rem = x - quot * y
where quot
is "a rounded quotient" of the division x
by y
.
For truncatingRemainder
, quot
is the quotient
rounded towards zero, and for remainder
, quot
is the quotient
rounded towards the nearest integer.
The result of truncatingRemainder
has always the same sign
as the dividend, this need not be the case for remainder
.
If both x
and y
are exactly representable as an integer
then the result of
x.truncatingRemainder(dividingBy: y)
is the same as
Int(x) % Int(y)
with the integer remainder operator %
.
What does % is unavailable: Use truncatingRemainder instead mean?
CMTimeGetSeconds()
returns a floating point number (Float64
akaDouble
). 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
truncatingRemainder(dividingBy: ) returning nonZero remainder even if number is completely divisible
As usual (see https://floating-point-gui.de), this is caused by the way numbers are stored in a computer.
According to the docs, this is what we expect
let price = //
let tick = //
let r = price.truncatingRemainder(dividingBy: tick)
let q = (price/tick).rounded(.towardZero)
tick*q+r == price // should be true
In the case where it looks to your eye as if tick
evenly divides price
, everything depends on the inner storage system. For example, if price
is 0.4
and tick
is 0.04
, then r
is vanishingly close to zero (as you expect) and the last statement is true.
But when price
is 0.5
and tick
is 0.05
, there is a tiny discrepancy due to the way the numbers are stored, and we end up with this odd situation where r
, instead of being vanishingly close to zero, is vanishing close to tick
! And of course the last statement is then false.
You'll just have to compensate in your code. Clearly the remainder cannot be the divisor, so if the remainder is vanishingly close to the divisor (within some epsilon), you'll just have to disregard it and call it zero.
You could file a bug on this but I doubt that much can be done about it.
Okay, I put in a query about this and got back that it behaves as intended, as I suspected. The reply (from Stephen Canon) was:
That's the correct behavior. 0.05 is a Double with the value 0.05000000000000000277555756156289135105907917022705078125. Dividing 0.5 by that value in exact arithmetic gives 9 with a remainder of 0.04999999999999997501998194593397784046828746795654296875, which is exactly the result you're seeing.
The only rounding error that occurs in your example is in the division price/tick, which rounds up to 10 before your
.rounded(.towardZero)
has a chance to take effect. We'll add an API to let you do something likeprice.divided(by: tick, rounding: .towardZero)
at some point, which will eliminate this rounding, but the behavior oftruncatingRemainder
is precisely as intended.You really want to have either a decimal type (also on the list of things to do) or to scale the problem by a power of ten so that your divisor become exact:
1> let price = 50.0
price: Double = 50
2> let tick = 5.0
tick: Double = 5
3> let r = price.truncatingRemainder(dividingBy: tick)
r: Double = 0
Swift Double.remainder(dividingBy:) returning negative value
Please, read the documentation carefully:
For two finite values x and y, the remainder r of dividing x by y satisfies x == y * q + r, where q is the integer nearest to x / y. If x / y is exactly halfway between two integers, q is chosen to be even. Note that q is not x / y computed in floating-point arithmetic, and that q may not be representable in any available integer type.
(emphasis mine)
You want to use truncatingRemainder(dividingBy:) instead:
let num = 32.0
let value = Double(num)
.truncatingRemainder(dividingBy: 12)
print(value) // 8
What is the modulus operator (%) in swift 3?
You can simply follow the diagnostic message:
let randomIndex = Int(drand48().truncatingRemainder(dividingBy: Double(alphabetColors.count)))
Or using arc4random_uniform(_:)
would be a better alternative.
let randomIndex = Int(arc4random_uniform(UInt32(alphabetColors.count)))
How do I get rid of % is unavailable: Use truncatingRemainder instead ?
just do the following...
let seconds = watch.elapsd.truncatingRemainder(dividingBy: 60)
let ten0fseconds = (watch.elapsd * 10).truncatingRemainder(dividingBy: 10)
Negative number modulo in swift
The Swift remainder operator %
computes the remainder of
the integer division:
a % b = a - (a/b) * b
where /
is the truncating integer division. In your case
(-1) % 3 = (-1) - ((-1)/3) * 3 = (-1) - 0 * 3 = -1
So the remainder has always the same sign as the dividend (unless
the remainder is zero).
This is the same definition as required e.g. in the C99 standard,
see for example
Does either ANSI C or ISO C specify what -5 % 10 should be?. See also
Wikipedia: Modulo operation for an overview
how this is handled in different programming languages.
A "true" modulus function could be defined in Swift like this:
func mod(_ a: Int, _ n: Int) -> Int {
precondition(n > 0, "modulus must be positive")
let r = a % n
return r >= 0 ? r : r + n
}
print(mod(-1, 3)) // 2
Related Topics
Swift Uisearchcontroller Wired Up in Core Data Project, App Runs, But Search Not Updating
Swift 4.2+ Seeding a Random Number Generator
Case Insensitive Dictionary in Swift
Iaps Actually Validating the Receipt (Swift)
Stopping an Running Skaction - Sprite Kit
Cannot Form Weak Reference to Instance of Class Nstextview
How to Get the Yaw, Pitch, Roll of an Aranchor in Absolute Terms
Xcode 6 Beta/Swift - Playground Not Updating
Skaction Runaction Does Not Execute Completion Block
How to Quit Swift Repl Without Using Ctrl-D
Enable + Disable Auto-Layout Constraints
Show/Hide Password - How to Add This Feature
Swiftui: Detect Finger Position on MAC Trackpad