Analog of c++ std::remainder?

I looking for Rust equivalent of std::remainder.

In compare to Rust % and C++ std::fmod where 46.0 % 90.0 = 46.0,
std::reminder(46.0, 90.0) gives -44.0.

So some crate with such functionality, or may be stdlib?

You could define this:

fn remainder(a: f32, b: f32) -> f32 {
    let n = (a/b).round();
    a - b*n
}
2 Likes

No, f64::rem_euclid(46.0, 90.0) return 46.0 as 46.0 % 90.0, while std::remainder return -44.0.

Maybe I'm missing something, I've not done maths since 10 years+, but would :

-f64::rem_euclid(90.0, 46.0);

be wrong ?

I am not sure what do you mean, if you suggest to use

fn rust_reminder(x: f64, y: f64) { -f64::rem_euclid(y, x) }

then, this would be wrong. std::remainder(44, 90) gives 44,
while -f64::rem_euclid(90.0, 44.0) gives -2. That is completely wrong.

@alice solution is right except when infinity is present and for the sign of the reamainder of -0.0.
C++ handling of inifinity seems weird to me, because it implies 0*inf = 0.
I also suspect some differences when half-way rounding occurs.

  • From cppreference: The IEEE floating-point remainder of the division operation x/y calculated by this function is exactly the value x - n*y, where the value n is the integral value nearest the exact value x/y. When |n-x/y| = ½, the value n is chosen to be even.
  • While the round functions in rust rounds half-way cases away from 0.0.

This doesn't always work correctly. For instance: remainder(16777216.0, 3.0) is supposed to return 1, but returns -2.

Replace round with trunc.

It works with f64 instead of f32.
In fact with f32 a/b is equal to 5592405.5 (it's 5592405.333 for f64) which is the halfway case. Rust goes away from 0 when rouding a/b so it chooses 5592406, while c++ is supposed to choose the even number (within std::remainder, round in c++ works like round in rust) 5592406 but it seems to choose 5592405 (that's odd :smile: ).

You're misintepreting the C++ spec. std::remainder is not supposed to do any floating point approximating. It always returns the mathematically precise answer.

The exact value is 5592405 + 1/3, the integral value nearest that is 5592405, and the answer is 16777216 - 3 * 5592405 = 1.0 precisely.

1 Like

Suffice to say from the responses above, rust's standard library does not provide this function. However, rust is typically compiled with libm, which does provide the function. One can link the symbol from that library if they wish.

Playground

mod ffi {
    use std::os::raw::c_float;

    #[link(name = "m")]
    extern {
        pub fn remainderf(from: c_float, to: c_float) -> c_float;
    }
}
fn remainder(from: f32, to: f32) -> f32 {
    unsafe { ffi::remainderf(from, to) }
}

fn main() {
    assert_eq!(remainder(16777216.0, 3.0), 1.0);
}

(this might cause trouble when compiling to certain targets, e.g. embedded? I almost exclusively compile to x86-64 so I would not know)

If somebody really feels that this is something the standard library ought to provide, an issue can be created on the rust repository.

1 Like

Out of interest, what are some use cases for the std::remainder semantics? I find Rust's rem_euclid very useful, but don't think I've ever run into a problem where having rem(46.0, 90.0) yield -44.0 would be useful.

A practical use case: std::remainder(x, 2 * pi) will normalize an angle to between -pi and pi, that's sometimes useful.

Also std::remainder can always be represented exactly with no rounding error, whereas rem_euclid has a rounding error, e.g. (-1.0f64).rem_euclid(2.0f64.powi(100)) will round.

3 Likes

remainder is one of the functions that are "required" by the IEEE 754-2008 standard, so for that reason it might make sense to add it.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.