What is the difference between the rest operator (%) in Python and Rust?

Asked

Viewed 123 times

4

Writing a small program in Rust, I noticed that the operator results % are different from what I get in Python for negative numbers. For example, in Python -4 % 26 returns 22, but in Rust:

fn main() {
    println!("{}", (-4 % 26));
}

Returns -4!

I looked at the documentation of the Rust operators and, it appears, the % is of remainder even, just like in Python.

Why the difference?

P.S: For context, in this part of the code I am forcing the number of interest to have a value between 0 and 25. In Python the strategy of using the remainder works, but in Rust, no.

  • 1

    The logic of Python is explained here. I don’t know about Rust, but it seems to me after same idea of Javascript. Remembering that remainder and module are 2 different concepts, and for positive numbers they often get confused because the results are the same (many languages, even, also confuse the names, say they implement one when it is actually another). Other related link: https://answall.com/q/446169/112052

  • 2

    The difference is that, in Python, the % is not remainder, but yes module. In Rust, the % yes, indeed, remainder. The definitions of mod and rem are different, so that arises this discrepancy.

  • 1

    Thanks for the clarifications, guys. For those who come here for the same reason, know that it is possible to get the module in Rust using (-4).rem_euclid(26)

1 answer

4


The basic premise is that although the symbol is the same, the meaning may be different.

Operator Parlance Semantics
% Rust Remainder
% Python Modulus

This difference occurs among several other programming languages as well. There are languages that choose names like rem and mod, which removes the ambiguity brought by %.

There are various ways to define the "Operation Module" on computers. The difference arises when computing the quotient. The two most common ones are:

mod(a, b) = a - b * floor(a / b)
rem(a, b) = a - b * trunc(a / b)

Realize that the mod round the result of the division down. O rem, however, round the number to zero. See the difference between negative and positive numbers in 1.5 and -1.5:

 floor(1.5) =  1, trunc( 1.5) =  1
floor(-1.5) = -2, trunc(-1.5) = -1

Then, in case one of the two arguments provided is less than zero, these definitions open margin for difference between their respective results.

Mathematical definition

To Wikipedia defines "Operation Module" thus:

Data two positive numbers, a and n, a modulo n is the rest of the Euclidean division of a for n, where a is the dividend and n the divisor.

When exactly one of a or n is negative, the definition naïve failure and programming languages differ in how these values are defined.

Mathematically, the result of the module operation is a equivalence class and any member of that class may be chosen as representative. Commonly, however, the lesser positive rest is chosen.

As other conventions of how to choose this representative are possible, the difference arises when implementing an algorithm to determine the rest of the Euclidean division. And this algorithm will depend on the programming language, since there is no consensus - all are valid according to mathematical identity. Both mod as rem are valid, although they produce different results.

See all the variations here and the most common implementations by programming language.

Differences in applications

One can understand mod as an operation that maps any number to a well-defined set of values. See the application x mod 3 for x from -5 to 5:

-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5       (valores de x)
 1,  2,  0,  1,  2, 0, 1, 2, 0, 1, 2       (resultados para `x mod 3`)

See that the result is cyclic and is in the whole set [0, 3[.

However, when using rem, we have a different result:

-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5       (valores de x)
-2, -1,  0, -2, -1, 0, 1, 2, 0, 1, 2       (resultados para `x rem 3`)

See that, for rem, the set of possible results depends on the signal of the operands, which removes the "cyclic rigor" that exists in mod.

Most languages offer as much rem how much mod (not necessarily with these names), since, as the results vary, the use can also be impacted. In some situations, only mod fits. In others, only rem resolve. When past operands are guaranteed to have the same signal, there is no difference.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.