Negative number modulo in swift

前端 未结 4 666
攒了一身酷
攒了一身酷 2020-11-30 11:33

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?

相关标签:
4条回答
  • 2020-11-30 11:56

    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
    
    0 讨论(0)
  • 2020-11-30 12:03

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

    0 讨论(0)
  • 2020-11-30 12:07

    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)
    }
    
    0 讨论(0)
  • 2020-11-30 12:08

    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.

    0 讨论(0)
提交回复
热议问题