Существует стандартная идиома для проверки диапазона с одной инструкцией сравнения. Это идет как:
(unsigned)x - a <= (unsigned)b - a /* a <= x <= b */
(unsigned)x - a < (unsigned)b - a /* a <= x < b */
В качестве распространенного примера (эта версия, если isdigit
гарантированно верна по стандарту):
(unsigned)ch - '0' < 10
Если ваш исходный тип больше int
(например, long long
), вам нужно будет использовать более крупные типы без знака (например, unsigned long long
). Если a
и b
являются константами или уже имеют тип без знака, или если вы знаете, что b-a
не будет переполнен, вы можете опустить приведение из b
.
Чтобы этот метод работал, естественно, вы должны иметь a<=b
, а типы / значения должны быть такими, чтобы исходное выражение (т.е. a <= x && x <= b
или подобное) действовало математически правильно. Например, если x
было подписано, а b
без знака, x<=b
может быть оценено как ложное при x=-1
и b=UINT_MAX-1
. Пока все ваши исходные типы имеют подпись или меньше, чем тип без знака, к которому вы применяете, это не проблема.
Что касается того, как работает этот "трюк", то после уменьшения по модулю UINT_MAX+1
чисто определяется, лежит ли x-a
в диапазоне от 0 до b-a
.
В вашем случае, я думаю, что следующее должно работать нормально:
(unsigned)i + threshold > 2U * threshold;
Если threshold
не изменяется между итерациями цикла, компилятор, вероятно, может хранить в регистрах threshold
и 2U*threshold
.
Говоря об оптимизации, хороший компилятор должен оптимизировать исходный тест диапазона для использования арифметики без знака, если он знает, что ограничения выполнены. Я подозреваю, что многие делают это с константами a
и b
, но, возможно, не с более сложными выражениями. Тем не менее, даже если компилятор может оптимизировать его, идиома (unsigned)x-a<b-a
все еще чрезвычайно полезна в макросах, где вы хотите убедиться, что x
вычисляется ровно один раз.