假设我在给定范围内有一个位置pos,这样:
0 <=位置<范围
范围内的这个位置可以包含在两种不同的上下文中,一种是范围是整数值,即pos < range < 2 31,另一种是范围是长整数,即高达pos < range < 2 63 . 如果我想在这些上下文之间移动,我需要将位置缩放到新范围,以便正确地向下舍入到最接近的(长)整数值。所以,从技术上讲,我想做的就是:
pos new = floor( pos old * range new / range old )
不幸的是,这种直截了当的方法并不能解决问题,因为如果我先进行乘法运算,它会溢出(因为pos old * range new可以大到 ~2 94),或者如果我先进行除法运算,则会出现舍入错误。使用浮点值进行数学运算通常也无济于事,因为它们不能提供足够的精度位,因此也可能导致不正确的舍入(我只有双精度可用)。
我找到了一种从整数范围正确缩放到长整数范围的方法:
public long scaleUp(int oldPos, int oldRange, long newRange) {
return (newRange / oldRange) * oldPos +
(newRange % oldRange) * oldPos / oldRange;
}
这确保了计算在任何时候都不会超出长整数的限制,也不会由于过早舍入而失去准确性(模数捕获在第一次除法中舍入丢失的部分)。
我现在想弄清楚的是一种进行反向缩放的方法:
public int scaleDown(long oldPos, long oldRange, int newRange) {
return ??? ;
}
不确定这是否应该比其他功能更困难,但不知何故我没有看到它。
几点说明:
- 我想避免使用浮点运算,因为我总是很难说服自己,由于四舍五入,给定公式确实不可能在一些非常罕见的情况下产生意想不到的结果
- 我宁愿不使用 BigInteger 库
- 虽然这里的代码示例是 Java,但这确实是一个与语言无关的问题