Truncatingremainder VS Remainder in Swift

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 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

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 like price.divided(by: tick, rounding: .towardZero) at some point, which will eliminate this rounding, but the behavior of truncatingRemainder 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



Leave a reply



Submit