C++ функции для целочисленного деления с четко определенной стратегией округления
Я хочу что-то в C++, что позволяет мне делать эффективное целочисленное деление с указанным поведением округления, что-то вроде этого:
div_down(-4,3) ==> -2
div_up(4,3) ==> 2
div_to_zero(-4,3) ==> -1
div_to_nearest(5,3) ==> 2
Мне бы хотелось, чтобы он определял поведение целевой машины во время компиляции и генерировал соответствующие оптимальные реализации. Нечто подобное для модуля также было бы неплохо, абстрагируя неопределенное поведение для отрицательных операндов во время компиляции.
Это существует?
Если нет, каков хороший способ сделать это? Я могу придумать несколько возможных подходов:
- Попробуйте реализовать их как отдельные выражения, которые статически оптимизируют
- Используйте константные выражения для определения поведения цели и выбора из нескольких реализаций, например, с использованием шаблонов (но как именно?)
3 ответа
Это то, что у меня есть, с предварительным условием d > 0. Кажется, что все они работают, но можно ли их упростить?
int div_down(int n, int d) {
if (n < 0) {
return -((d - n - 1) / d);
} else {
return n / d;
}
}
int div_up(int n, int d) {
if (n < 0) {
return -(-n / d);
} else {
return (n + d - 1) / d;
}
}
int div_to_zero(int n, int d) {
return n / d;
}
int div_to_nearest(int n, int d) {
if (n < 0) {
return (n - d/2 + 1) / d;
} else {
return (n + d/2) / d;
}
}
Старый пост, но здесь это идет. Примите и оцените, если хотите.
int div_to_zero(int n, int d) { return n / d; }
//as per C++11 standard note 80
int div_up(int n, int d) {
return n / d + (((n < 0) ^ (d > 0)) && (n % d));
} //i.e. +1 iff (not exact int && positive result)
int div_down(int n, int d) {
return n / d - (((n > 0) ^ (d > 0)) && (n % d));
} //i.e. +1 iff (not exact int && negative result)
int div_to_nearest(int n, int d) {
return (2*n - d + 2*(true&&(n<0^d>0))*d) / (2*d);
} //i.e. +-0.5 as per pre-rounding result sign, then div_to-zero
//it however rounds numbers like +/- 3.5 towards 0 and not even.
Примечание: большинство современных компиляторов будут использовать одну операцию деления для n/d и n%d, используемых совместно. Таким образом, с точки зрения производительности, это лучшее сокращение памяти.
Последний черновик C++11, n3242, который практически идентичен фактическому стандарту C++11, говорит об этом в 5.6 пункт 4 (стр. 118):
Для целочисленных операндов оператор / дает алгебраический фактор с любой отброшенной дробной частью; (см. примечание 80)
Примечание 80 штатов (обратите внимание, что примечания являются ненормативными):
80) Это часто называют усечением до нуля.
Для полноты, пункт 4 далее заявляет:
если частное a/b представимо в типе результата, (a/b)*b + a%b равно a.
который может показывать, что требуется знак a%b
быть таким же, как знак a
(когда не ноль).
Примечание: действующий стандарт C++11 юридически не доступен онлайн. Тем не менее, проекты являются. К счастью, различия между последним проектом (N3242) и фактическим стандартом невелики. Смотрите этот ответ.
ПРИМЕЧАНИЕ: я не уверен, какие компиляторы придерживаются стандарта C++11.
Так div_to_zero()
является регулярным /
разделение.
Боюсь, что для других функций вам придется проверить признаки a
а также b
и настроить соответственно. Иногда дополнительная проверка a%b
равно нулю может быть необходимо. Таким образом, мы рассматриваем 12 тестовых случаев для каждой функции здесь (3 для знака или нулевого значения a, умножить на 2 для знака b, умножить на 2 a%b
равен нулю или нет).
Это слишком много для меня, чтобы понять прямо сейчас, так что, может быть, кто-то еще прыгнет и даст правильный ответ.
Мне известно, что я не ответил на ваш вопрос, но приведенная выше информация казалась ценной и была слишком большой, чтобы уместиться в комментарии.