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 равен нулю или нет).

Это слишком много для меня, чтобы понять прямо сейчас, так что, может быть, кто-то еще прыгнет и даст правильный ответ.

Мне известно, что я не ответил на ваш вопрос, но приведенная выше информация казалась ценной и была слишком большой, чтобы уместиться в комментарии.

Другие вопросы по тегам