Как кодировать оператор по модулю ( % ) в C / C++ / Obj-C, который обрабатывает отрицательные числа
одна из моих любимых ненавистей к C-производным языкам (как математик) заключается в том, что
(-1) % 8 // comes out as -1, and not 7
fmodf(-1,8) // fails similarly
каково лучшее решение?
C++ допускает возможность перегрузки шаблонов и операторов, но оба они для меня мутные воды. примеры с благодарностью приняты.
15 ответов:
прежде всего я хотел бы отметить, что вы не можете даже полагаться на то, что
(-1) % 8 == -1
. единственное, на что вы можете положиться, это(x / y) * y + ( x % y) == x
. Однако независимо от того, является ли остаток отрицательным,реализация-определено.теперь зачем использовать шаблоны здесь? Перегрузка для интов и лонгов подойдет.
int mod (int a, int b) { int ret = a % b; if(ret < 0) ret+=b; return ret; }
и теперь вы можете назвать его как mod(-1,8), и он будет казаться 7.
Edit: я нашел ошибку в моем коде. Это не будет работать, если b отрицательно. Так что я думаю, что это лучше:
int mod (int a, int b) { if(b < 0) //you can check for b == 0 separately and do what you want return mod(a, -b); int ret = a % b; if(ret < 0) ret+=b; return ret; }
ссылка: C++03 пункт 5.6 пункт 4:
оператор binary / дает частное, а оператор binary % дает остаток от деления первого выражения на второе. Если второй операнд / или % равен нулю, поведение не определено; в противном случае (a/b)*b + a%b равно a. если оба операнда неотрицательны, то остаток неотрицателен; если нет, то знак остатка реализация-определено.
вот функция C, которая обрабатывает положительные или отрицательные целочисленные или дробные значения для обоих операндов
#include <math.h> float mod(float a, float N) {return a - N*floor(a/N);} //return in range [0, N)
это, безусловно, самое элегантное решение с математической точки зрения. Однако я не уверен, что он надежен в обработке целых чисел. Иногда ошибки с плавающей запятой ползут при преобразовании int - > fp - > int.
Я использую этот код для non-int s и отдельную функцию для int.
Примечание: нужно поймать N = 0!
тестер код:
#include <math.h> float mod(float a, float N) { float ret = a - N * floor (a / N); printf("%f.1 mod %f.1 = %f.1 \n", a, N, ret); return ret; } int main (char* argc, char** argv) { printf ("fmodf(-10.2, 2.0) = %f.1 == FAIL! \n\n", x); float x; x = mod(10.2f, 2.0f); x = mod(10.2f, -2.0f); x = mod(-10.2f, 2.0f); x = mod(-10.2f, -2.0f); return 0; }
(Примечание: Вы можете скомпилировать и запустить его прямо из CodePad:http://codepad.org/UOgEqAMA)
выход:
fmodf(-10.2, 2.0) = -0.20 == не получится!
10.2 mod 2.0 = 0.2
10.2 mod -2.0 = -1.8
-10.2 mod 2.0 = 1.8
-10.2 mod -2.0 = -0.2
Я только что заметил, что Bjarne Stroustrup labels
%
Как остаток оператор не оператора по модулю.Я бы поспорил, что это его формальное имя в спецификациях ANSI C & C++, и что злоупотребление терминологией закралось. Кто-нибудь знает это правда?
но если это так, то функция fmodf() C (и, вероятно, другие) очень вводят в заблуждение. они должны быть помечены fremf () и т. д.
для целых чисел это просто. Просто сделай
(((x < 0) ? ((x % N) + N) : x) % N)
где я предполагаю, что
N
является положительным и представимым в видеx
. Ваш любимый компилятор должен быть в состоянии оптимизировать это, так что он заканчивается только в одной операции mod в ассемблере.
лучшим решением 1 для математика является использование Python.
перегрузка оператора C++ имеет мало общего с этим. Нельзя перегружать операторы для встроенных типов. Что вы хотите-это просто функция. Конечно, вы можете использовать шаблон C++ для реализации этой функции для всех соответствующих типов с помощью всего 1 фрагмента кода.
стандартная библиотека C содержит
fmod
, Если я правильно помню имя, для типов с плавающей запятой.для целых чисел можно определить Шаблон функции C++, который всегда возвращает неотрицательный остаток (соответствующий евклидову делению) as ...
#include <stdlib.h> // abs template< class Integer > auto mod( Integer a, Integer b ) -> Integer { Integer const r = a%b; return (r < 0? r + abs( b ) : r); }
... и просто напишите
типmod(a, b)
вместоa%b
.Integer
должен быть целочисленным типом со знаком.если вы хотите общее математическое поведение, где знак остатка совпадает со знаком делителя, то вы можете сделать, например,
template< class Integer > auto floor_div( Integer const a, Integer const b ) -> Integer { bool const a_is_negative = (a < 0); bool const b_is_negative = (b < 0); bool const change_sign = (a_is_negative != b_is_negative); Integer const abs_b = abs( b ); Integer const abs_a_plus = abs( a ) + (change_sign? abs_b - 1 : 0); Integer const quot = abs_a_plus / abs_b; return (change_sign? -quot : quot); } template< class Integer > auto floor_mod( Integer const a, Integer const b ) -> Integer { return a - b*floor_div( a, b ); }
... С тем же ограничением на
Integer
это подпись тип.
1 потому что целочисленное деление Python округляется до отрицательной бесконечности.
самая простая общая функция для нахождения положительного модуля будет такой- Он будет работать как на положительных, так и на отрицательных значениях x.
int modulo(int x,int N){ return (x % N + N) %N; }
О, я ненавижу % дизайн для этого тоже....
вы можете конвертировать дивиденды в unsigned так как:
unsigned int offset = (-INT_MIN) - (-INT_MIN)%divider result = (offset + dividend) % divider
где смещение ближе всего к (- INT_MIN) кратному модулю, поэтому добавление и вычитание его не изменит по модулю. Обратите внимание, что он имеет беззнаковый тип и результат будет целочисленным. К сожалению, он не может правильно преобразовать значения INT_MIN...(- смещение-1) так как они вызывают арифметическое переполнение. Но этот метод имеет номере только один дополнительный арифметика в деятельность (и никакие conditionals) при работе с постоянн рассекателем, поэтому она годна к употреблению в DSP-подобных применениях.
есть особый случай, где делитель 2N (целочисленная степень двойки), для которой по модулю можно вычислить с помощью простой арифметики и побитовой логики как
dividend&(divider-1)
x mod 2 = x & 1 x mod 4 = x & 3 x mod 8 = x & 7 x mod 16 = x & 15
более распространенным и менее сложным способом является получение по модулю с помощью этой функции (работает только с положительным делителем):
int mod(int x, int y) { int r = x%y; return r<0?r+y:r; }
этот только правильный результат, если он отрицательный.
также вы можете трик:
(p%q + q)%q
это очень короткий, но использовать два %-s, которые обычно медленно.
Я считаю, что другое решение этой проблемы будет использовать переменные типа long вместо int.
Я просто работал над некоторым кодом, где оператор % возвращал отрицательное значение, которое вызывало некоторые проблемы (для генерации однородных случайных величин на [0,1] вы действительно не хотите отрицательных чисел :)), но после переключения переменных на тип long все работало гладко, и результаты соответствовали тем, которые я получал при запуске одного и того же кода в python (важно для меня, поскольку я хотел иметь возможность генерировать одни и те же "случайные" числа на нескольких платформах.
/* Warning: macro mod evaluates its arguments' side effects multiple times. */ #define mod(r,m) (((r) % (m)) + ((r)<0)?(m):0)... или просто привыкнешь любого представителя класса эквивалентности.
вот новый ответ на старый вопрос, на основании этого Microsoft Research paper и ссылки в нем.
обратите внимание, что начиная с C11 и C++11, семантика
div
стало усечение к нулю (см.[expr.mul]/4
). Кроме того, дляD
разделить наd
в C++11 гарантирует следующее соотношениеqT
а остальныеrT
auto const qT = D / d; auto const rT = D % d; assert(D == d * qT + rT); assert(abs(rT) < abs(d)); assert(signum(rT) == signum(D));
здесь
signum
карты -1, 0, +1, в зависимости от того, аргумент чем 0 (см. это Q & A исходный код).с усеченными дивизии, знак остатка равен знаку дивиденда
D
, т. е.-1 % 8 == -1
. C++11 также предоставляетstd::div
функция, которая возвращает структуру с членамиquot
иrem
по данным отдела усе.возможны и другие определения, например так называемые пол отдела можно определить в терминах из встроенного усеченного деления
auto const I = signum(rT) == -signum(d) ? 1 : 0; auto const qF = qT - I; auto const rF = rT + I * d; assert(D == d * qF + rF); assert(abs(rF) < abs(d)); assert(signum(rF) == signum(d));
с полом дивизии, знак остатка равен знаку делителя
d
. В таких языках, как Haskell и Oberon, есть встроенные операторы для разделения полов. В C++ нужно написать функцию, используя приведенные выше определения.еще один способ-это Евклидово подразделение, который также может быть определен в терминах встроенного усеченного подразделение
auto const I = rT >= 0 ? 0 : (d > 0 ? 1 : -1); auto const qE = qT - I; auto const rE = rT + I * d; assert(D == d * qE + rE); assert(abs(rE) < abs(d)); assert(signum(rE) != -1);
С евклидовым делением,знак остатка всегда положительно.
пример шаблона для C++
template< class T > T mod( T a, T b ) { T const r = a%b; return ((r!=0)&&((r^b)<0) ? r + b : r); }
С помощью этого шаблона возвращаемый остаток будет равен нулю или иметь тот же знак, что и делитель (знаменатель) (эквивалент округления в сторону отрицательной бесконечности), вместо того, чтобы поведение c++ остатка было равно нулю или имело тот же знак, что и дивиденд (числитель) (эквивалент округления в сторону нуля).