3

I'm representing numbers as ratios of signed 64-bit integers using the num-rational crate's Rational64 type. I'm trying to round a number down to the next multiple of another number, and I'm getting integer overflow issues when I do it in either of the two obvious ways. Note that both of the numbers may be fractions.

Normalize to an integer

extern crate num_rational;
extern crate num_traits;

use num_rational::Rational64;
use num_traits::identities::Zero;

fn round(mut n: Rational64, increment: Rational64) -> Rational64 {
    let rem = n % increment;
    if !rem.is_zero() {
        // normalize to a multiple of the increment, round down
        // to the next integer, and then undo the normalization
        n = (n * increment.recip()).trunc() * increment;
    }
    n
}

fn main() {
    let a = Rational64::new(10_000_676_909_441, 8_872_044_800_000_000);
    let b = Rational64::new(1, 1_000_000);
    let c = round(a, b);
    println!("{}", c);
}

(playground)

Subtract the remainder

extern crate num_rational;
extern crate num_traits;

use num_rational::Rational64;
use num_traits::identities::Zero;

fn round(mut n: Rational64, increment: Rational64) -> Rational64 {
    let rem = n % increment;
    if !rem.is_zero() {
        n -= rem;
    }
    n
}

fn main() {
    let a = Rational64::new(10_000_676_909_441, 8_872_044_800_000_000);
    let b = Rational64::new(1, 1_000_000);
    let c = round(a, b);
    println!("{}", c);
}

(playground)

Is there a way to make it so that n is rounded down to a multiple of increment such that integer overflow is less likely? It's fine if I have to extract the numerator and denominator (both Rust i64 types) and do math on them directly.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
joshlf
  • 21,822
  • 11
  • 69
  • 96
  • 3
    Please note the difference between your original code and the current code. This is what we mean when we ask for a MCVE. Having a MCVE prevents your question from being downvoted (or closed) and also **greatly** increases the chance of a good answer. By providing a MCVE *once*, you avoid forcing *every* potential answerer from having to re-create your entire setup (guessing types, guessing values, etc.). The process of reduction helps you understand the core problem, and it makes your question more useful to people who search for the same thing in the future. It's a win for basically everyone. – Shepmaster Feb 10 '18 at 18:12
  • 1
    Reliably doing 128-bit arithmetic using 64-bit types is tricky. You'd probably be better off turning the arguments into `BigRational`s, doing the arithmetic, then converting back. – NovaDenizen Feb 13 '18 at 01:30
  • 1
    Yeah that's what I ended up doing. I was afraid of the performance hit, but it turned out to be acceptable for my use case. It'd still be interesting to see how this might be done, though. – joshlf Feb 14 '18 at 02:23

0 Answers0