Допустим, у меня есть позиция pos в пределах заданного диапазона, так что:
0 ‹= позиция ‹ диапазон
Эта позиция в пределах диапазона может состоять из двух разных контекстов: в одном диапазон представляет собой целочисленное значение, т. е. pos ‹ range ‹ 231, и другой, где диапазон представляет собой длинное целое число, то есть до pos ‹ range ‹ 263. Если я хочу перемещаться между этими контекстами, мне нужно масштабировать положение до нового диапазона, чтобы оно было правильно округлено до ближайшего (длинного) целочисленного значения. Итак, технически, все, что я хочу сделать, это:
позицияновая = пол( позициястарая * диапазонновая sub> / диапазонстарый )
К сожалению, этот прямой подход не помогает, поскольку он либо переполняется (как posold * rangenew > может достигать ~294), если я сначала выполняю умножение, или выдает ошибки округления, если я сначала выполняю деление. Использование значений с плавающей запятой для выполнения математических операций в целом также не помогает, поскольку они не обеспечивают достаточной точности и, следовательно, также могут привести к неправильному округлению (у меня доступна только двойная точность).
Я нашел способ правильно масштабировать диапазон целых чисел до диапазона длинных целых чисел:
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, на самом деле это вопрос, не зависящий от языка.
p * r // R
) - person   schedule 04.07.2013