19

How does modulo of negative numbers work in swift ? When i did (-1 % 3) it is giving -1 but the remainder is 2. What is the catch in it?

Ashutosh Pandey
  • 238
  • 2
  • 11

5 Answers5

33

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
Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
6

From the Language Guide - Basic Operators:

Remainder Operator

The remainder operator (a % b) works out how many multiples of b will fit inside a and returns the value that is left over (known as the remainder).

The remainder operator (%) is also known as a modulo operator in other languages. However, its behavior in Swift for negative numbers means that it is, strictly speaking, a remainder rather than a modulo operation.

...

The same method is applied when calculating the remainder for a negative value of a:

-9 % 4   // equals -1

Inserting -9 and 4 into the equation yields:

-9 = (4 x -2) + -1

giving a remainder value of -1.

In your case, no 3 will fit in 1, and the remainder is 1 (same with -1 -> remainder is -1).

dfrib
  • 70,367
  • 12
  • 127
  • 192
3

If what you are really after is capturing a number between 0 and b, try using this:

infix operator %%

extension Int {
    static  func %% (_ left: Int, _ right: Int) -> Int {
        if left >= 0 { return left % right }
        if left >= -right { return (left+right) }
        return ((left % right)+right)%right
    }
}

print(-1 %% 3) //prints 2

This will work for all value of a, unlike the the previous answer while will only work if a > -b.

I prefer the %% operator over just overloading %, as it will be very clear that you are not doing a true mod function.

The reason for the if statements, instead of just using the final return line, is for speed, as a mod function requires a division, and divisions are more costly that a conditional.

cdeerinck
  • 688
  • 6
  • 17
1

An answer inspired by cdeerinck, which sacrifices speed for simplicity, is this:

infix operator %%

extension Int {

    static  func %% (_ left: Int, _ right: Int) -> Int {
       let mod = left % right
       return mod >= 0 ? mod : mod + right
    }

}

I tested it with this little loop in a playground:

for test in [6, 5, 4, 0, -1, -2, -100, -101] {
    print(test, "%% 5", test %% 5)
}
MarkAurelius
  • 1,203
  • 1
  • 14
  • 27
  • There is discussion of it here, with alternative solutions: https://forums.swift.org/t/modulo-operation-in-swift/7018 . Martin R's answer is the same algorithm as mine, but he uses generics to make it more useful. – MarkAurelius May 28 '20 at 03:42
0

Martin has a great answer clarifying that Swifts % is a remainder operator rather than a modulo operator and provides a modulo implementation to handle the case of a negative remainder.

Swift also has a very useful function quotientAndRemainder on BinaryInteger:

func quotientAndRemainder(dividingBy rhs: Self) -> (quotient: Self, remainder: Self)

I've found it helpful to write a similar "modulo" dual for this with special handling for a negative remainder…

extension BinaryInteger {
    public func quotientAndModulo(dividingBy divider: Self) -> (quotient: Self, modulo: Self) {
        let (q, r) = quotientAndRemainder(dividingBy: divider)
        return r >= 0 ? (q, r) : (q - 1, divider + r)
    }
}
Benjohn
  • 13,228
  • 9
  • 65
  • 127