Предположим, что у нас есть 2 константы A
и B
и переменная i
, все 64-битные целые числа. И мы хотим вычислить простую общую арифметическую операцию, такую как:
i * A / B (1)
Чтобы упростить задачу, допустим, что переменная i
всегда находится в диапазоне [INT64_MIN*B/A, INT64_MAX*B/A]
, так что конечный результат арифметической операции (1) не переполняется (т.е. соответствует в диапазоне [INT64_MIN, INT64_MAX]
).
Кроме того, i
считается более вероятным в дружественном диапазоне Range1 = [INT64_MIN/A, INT64_MAX/A]
(то есть: близком к 0), однако i
может быть (менее вероятно) снаружи этот диапазон. В первом случае тривиальное целочисленное вычисление i * A
не будет переполняться (поэтому мы назвали диапазон дружественным); и в последнем случае тривиальное целочисленное вычисление i * A
будет переполняться, что приведет к ошибочному результату при вычислении (1).
Каким будет "самый безопасный" и "самый эффективный" способ вычисления операции (1) (где "безопаснее" означает: сохранение точности или, по крайней мере, достойной точности и где "наиболее эффективным" означает: наименьшее среднее время вычислений), если i
более вероятно в диапазоне дружественных Range1.
В настоящее время решение, реализованное в настоящее время в коде, является следующим:
(int64_t)((double)A / B * i)
какое решение является достаточно безопасным (без переполнения), хотя и неточным (прецизионные потери из-за двойного значения 53-разрядного ограничения) и довольно быстро, потому что двойное деление (double)A / B
предварительно вычисляется во время компиляции, позволяя вычислять только двойное умножение во время выполнения.