Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Rust's rem_euclid is still wrong for negative moduli. To see why, remember that we want n = remainder + quotient * divisor. Now look at the horrible mess that is div_euclid:

> In other words, the result is self / rhs rounded to the integer q such that self >= q * rhs. If self > 0, this is equal to round towards zero (the default in Rust); if self < 0, this is equal to round towards +/- infinity.

Python gets it right. Division is simply flooring division, and modulo is defined to satisfy n = remainder + quotient * divisor. That immediately gets you all the nice behavior such as (-1) % 10 = 9, and 1 % -10 = -9.



Can you clarify why is Rust's div_euclid and rem_euclid are incorrect? For reference, here's what Rust prints for this https://play.rust-lang.org/?version=stable&mode=debug&editio...

    fn main() {
        dbg!(1_i32.rem_euclid(-10));
        dbg!(-1_i32.rem_euclid(10));    
    }

    [src/main.rs:2] 1_i32.rem_euclid(-10) = 1
    [src/main.rs:3] -1_i32.rem_euclid(10) = -1

I found only this issue https://github.com/rust-lang/rust/issues/87970 but has no details

Here's the discussion that lead to the implementation of those functions, from more recent to least recent,

* the tracking issue https://github.com/rust-lang/rust/issues/49048

* the RFC https://github.com/rust-lang/rfcs/pull/2169

* the internals discussion https://internals.rust-lang.org/t/mathematical-modulo-operat...

It's baffling that Rust got this wrong..


The operator precedence is wrong on the second one: to take -1 mod 10, you'd want to write (-1_u32).rem_euclid(10), which gives the expected result of 9.


Oh, indeed. But, is the first one wrong then? But why should 1_i32.rem_euclid(-10) return -9 instead of 1?


Because always returning a positive result from the remainder is incredibly unnatural. You can get this behavior through Python's definition trivially using `a % abs(b)`, making rem_euclid less flexible as well.

The reason it's incredibly unnatural is not obvious when looking at the remainder in isolation. But the remainder is always one half of a pair: remainder and quotient. The quotient associated with rem_euclid is batshit insane.

The quotient associated with Python's modulo definition is simply floor(a/b).

Note that I'm wording it very strongly when I say it is wrong. It follows its spec faithfully and isn't 'bugged', its spec is just poorly and unnaturally chosen.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: