I'm doing math on wrapped integers (i32 type). As long as I work on i32 type only, it's easy. However, some of these calculations require floating point operations. When I'm going back from f64 to i32 type, I got my values casted with saturation. This is the standard behavior, but not what I need.

I don't know if it's optimal, but this should work:

fn wrap_to_i32(input: f64) -> i32 {
input.rem_euclid((u32::MAX as f64) + 1.0) as u32 as i32
}
#[test]
fn test() {
assert_eq!(wrap_to_i32(0 as f64), 0);
assert_eq!(wrap_to_i32(0x7FFF_FFFF as f64), 0x7FFF_FFFF);
assert_eq!(wrap_to_i32(-0x8000_0000 as f64), -0x8000_0000);
assert_eq!(wrap_to_i32(0x7_0000_1234_i64 as f64), 0x1234);
assert_eq!(wrap_to_i32(-0x7_0000_1234_i64 as f64), -0x1234);
}

The rem_euclid() wraps within a positive range, then casting u32 to i32 makes half of that range negative.

Note that the rounding behavior (what is done with fractional digits) is towards zero (truncation), which may not be most appropriate for your application. You can change this by adding a .floor(), .ceil(), or .round() as the first thing done to input.