Function returning quotient and remainder

Take a look at this code, this can be done efficiently (is there any function in std library which does this)

fn formatted_memory(kb: u64) -> String {
    let total_bytes = 1000 * kb;

    let (gib, rem_bytes) = (total_bytes / 1073741824, total_bytes % 1073741824);
    let (mib, rem_bytes) = (rem_bytes / 1048576, rem_bytes % 1048576);
    let (kib, bytes) = (rem_bytes / 1024, rem_bytes % 1024);

    format!("{gib}GiB {mib}MiB {kib}KiB {bytes}B")
}
1 Like

There is a div_rem function in the num-integer crate. It works with integer types such as u64 and u32.

I agree it should be on the stdlib as an inherent method of each integer type, even though the compiler is able to optimize away separate div and rem operations. I mean I think it's an useful API. (It would also be really nice if it was more than one method, such as div_rem and div_rem_euclid)

There is this issue in the RFC repo about it.

6 Likes

Thanks for helping me out!

By the way in this particular case all the divisors are power of two, so the compiler won't even emit division instructions and instead will use bitwise operations to shift and mask the dividend.

4 Likes

If you look at the code generated by the compiler with Compiler Explorer the calculus part is about 11 instructions, and as mentionned by @SkiFire13 it's only bitwise instructions. So this code is pretty efficient as it.

2 Likes

It's not necessary to have a dedicated function for this, because LLVM easily optimizes functions that use both / and %.

1 Like

I’ve wanted this function in the past too. The main utility to me is code clarity, not optimization. let (index, offset) = bit_index.div_rem(uzise::BITS); is less effort to read for me than let (index, offset) = (bit_index / usize::BITS, bit_index % usize::BITS);.

13 Likes

It’s also a bit clearer when you’re in the (rare) range where a constant is obvious enough that you don’t need to name it, but you’d still rather not repeat it. Extracting decimal digits is the one that comes to mind (div_rem(1000) gives you the first three digits and also the rest of ’em).

6 Likes

I looked into this last year. As Kornel said LLVM will happily optimise the separate operators.

1 Like