Let's say I have a position pos within a given range, such that:
0 <= pos < range
This position within the range can consist in two different contexts, one where the range is an integer value, i.e. pos < range < 231, and the other where the range is a long-integer, i.e. up to pos < range < 263. If I want to move between these contexts, I need to scale the position to the new range such that it is correctly rounded down to the nearest (long-)integer value. So, technically, all I want to do is:
pos new = floor( pos old * range new / range old )
Unfortunately, this straight forward approach doesn't do the trick since it either overflows (as pos old * range new can be as large as ~294) if I do the multiplication first or gives me rounding errors if I do the division first. Using floating point values to do the math doesn't help in general either, as they don't offer enough bits of precision and might therefore also lead to incorrect rounding (I only have double-precision available).
I found a way to scale from the integer range to the long-integer range correctly:
public long scaleUp(int oldPos, int oldRange, long newRange) {
return (newRange / oldRange) * oldPos +
(newRange % oldRange) * oldPos / oldRange;
}
This ensures that the calculation neither overflows the limits of a long-integer at any point, nor looses accuracy due to premature rounding (the modulus captures the part lost to rounding in the first division).
What I'm trying to figure out now, is a way to do the reverse scaling:
public int scaleDown(long oldPos, long oldRange, int newRange) {
return ??? ;
}
Not sure if this should be any more difficult than the other function, but somehow I'm not seeing it.
A couple of remarks:
- I'd like to avoid using floating point arithmetic, since I always find it very hard to convince myself that it is truly not possible for a given formula to yield an unexpected result in some highly infrequent cases due to rounding
- I'd rather not use a BigInteger library
- While the code-samples here are Java, this really is a language-agnostic question