How to Precisely Spread Numbers?

I want to spread a bunch of integers of primitive type T to a different range, typically T::MIN..=T::MAX. They might be percentages, i.e. add up to 100. Mathematically adjusting their sizes is easy: Say T is u8, then in this case let new = old * 255 / 100. Except the multiplication will overflow, whereas let new = old / 100 * 255 will first truncate to zero.

Obviously let new = old * 2.55 would be the solution, but it is not possible to mix types. Also I can’t generally convert old to float, because, even if we already had f128, floats always have less precision than an equal-sized integer.

I guess I’d need mul_rem<F: {float}>(self, factor: F) -> (Self, F) which would return the floor and the truncated fractional part of the product. (The sum of the floors will often fall short, so I’d then have to adjust: add 1 to some of those with the highest fractional parts.)

I want to do this in const, but I’m beginning to wonder whether this is at all generally possible (i.e. not only on numbers small enough to losslessly fit into float.)

Do the multiplication in u16:

 let new = u8::try_from(u16::from(old) * 255 / 100).unwrap();

Using the next size up works through u64. For u128 you could adapt this algorithm.

Here's a POC that it can be done in const. Note that I performed zero testing, so you should do your own review and make some test cases etc.

1 Like