Однострочное решение с постоянным временем:
Ладно, если вы рассчитываете вторую функцию для формы [min,max)
, это две строки, но достаточно близко - вы можете объединить их вместе в любом случае.
/* change to `float/fmodf` or `long double/fmodl` or `int/%` as appropriate */
/* wrap x -> [0,max) */
double wrapMax(double x, double max)
{
/* integer math: `(max + x % max) % max` */
return fmod(max + fmod(x, max), max);
}
/* wrap x -> [min,max) */
double wrapMinMax(double x, double min, double max)
{
return min + wrapMax(x - min, max - min);
}
Тогда вы можете просто использовать deltaPhase = wrapMinMax(deltaPhase, -M_PI, +M_PI)
.
Решения являются постоянными, то есть время, которое требуется, не зависит от того, как далеко ваше значение от [-PI,+PI)
- в лучшую сторонуили, что еще хуже.
Проверка:
Теперь я не ожидаю, что вы поверите мне на слово, поэтому приведу несколько примеров, включая граничные условия.Я использую целые числа для ясности, но он работает почти так же с fmod()
и с плавающей точкой:
- Положительный
x
: wrapMax(3, 5) == 3
: (5 + 3 % 5) % 5 == (5 + 3) % 5 == 8 % 5 == 3
wrapMax(6, 5) == 1
: (5 + 6 % 5) % 5 == (5 + 1) % 5 == 6 % 5 == 1
- Отрицательное
x
: - Примечание: Предполагается, что целое число по модулю копирует знак левой руки;если нет, вы получите вышеуказанный («положительный») случай.
wrapMax(-3, 5) == 2
: (5 + (-3) % 5) % 5 == (5 - 3) % 5 == 2 % 5 == 2
wrapMax(-6, 5) == 4
: (5 + (-6) % 5) % 5 == (5 - 1) % 5 == 4 % 5 == 4
- Границы:
wrapMax(0, 5) == 0
: (5 + 0 % 5) % 5 == (5 + 0) % 5 == 5 % 5 == 0
wrapMax(5, 5) == 0
: (5 + 5 % 5) % 5 == (5 + 0) % 5== 5 % 5 == 0
wrapMax(-5, 5) == 0
: (5 + (-5) % 5) % 5 == (5 + 0) % 5 == 5 % 5 == 0
- Примечание: Возможно
-0
вместо +0
для чисел с плавающей запятой.
Функция wrapMinMax
работает практически так же: перенос x
до [min,max)
аналогичен переносу x - min
до [0,max-min)
, а затем (повторному) добавлению min
крезультат.
Я не знаю, что произойдет с отрицательным максимумом, но не стесняйтесь проверить это сами!