Во многих языках программирования (C, C++, C#, Java, различные диалекты Паскаля, PHP, JavaScript) есть оператор вычисления остатка. Он действует очевидным, единственно верным образом для положительных значений аргумента (17 % 5 == 2), но для отрицательного делимого и положительного делителя он даёт отрицательный результат:
-17 % 5 == -2
Обычное применение оператора %, однако — для вычисления хэшей, индексов в кольцевом буфере, а также вычисление канонического представителя группы чисел, то есть, для представления отношения эквивалентности. Например, номер дня недели естественным образом вычисляется как остаток от деления «глобального» номера дня на 7. Проверка числа на нечётность также определяется остатком при делении на 2.
Однако, оператор % в том виде, как он реализован в упомянутых языках, непригоден без дополнительной обработки: нужна функция наподобие
int mod(int n, int d)
{
int result = n % d;
if (sign(result) * sign(d) < 0)
result += d;
return result;
}
которая обеспечивает положительный результат при положительном делителе.
У такой функции, в отличие от %, есть полезный инвариант:
mod(n + k * d, d) == mod(n, d)
(при условии, что вычисление обеих частей не приводят к переполнению).
Приведу несколько примеров.
Проверка на нечётность обычно выглядит так:
//bool odd = n % 2 == 1; // неправильно! bool odd = n % 2 != 0; // довольно искусственно
Но с новым оператором она работает проще:
bool odd = mod(n, 2) == 1; // как и ожидается.
Для вычисления bucket'а в хэш-таблице тоже применяется остаток:
int bucketIdx = object.GetHashCode() % buckets.Count; if (bucketIdx < 0) bucketIdx += buckets.Count;
или так (код из .NET 4.0)
int positiveHashCode = object.GetHashCode() & 7FFFFFFF;
int bucketIdx = positiveHashCode % buckets.Count;
В то же время
int bucketIdx = mod(object.GetHashCode(), buckets.Count);
сработал бы в любом случае.
Приведение угла в градусах к каноническому виду (от 0 до 360):
mod(angle, 360)
В радианах: mod(angle, 2*PI)
То же самое с % выглядит гораздо более тяжеловесно.
Внимание, вопрос: Нужен ли кому-то оператор % в том виде, как он определён в языке? Не лучше было бы, чтобы оператор % возвращал значение как у mod? Я предполагаю, что всякий раз, когда используется оператор %, на самом деле имеется в виду именно mod, и либо входные аргументы всегда положительны, либо используется поправка при отрицательном делимом, либо программа содержит баг.
Есть ли у кого-то примеры (из практики или теоретические), когда нужно именно существующее поведение оператора %?
Двое отвечающих справедливо замечают, что частное q и остаток r при делении n на d должны удовлетворять условию
n == q * d + r
Для того, чтобы это работало, можно переопределить и деление так, чтобы округление выполнялось всегда вниз: q == floor((double)n / (double)d). При этом нужное соотношение будет автоматически выполняться:
4 / 3 == 1 4 % 3 == 1 4 = 1 * 3 + 1
3 / 3 == 1 3 % 3 == 0 3 = 1 * 3 + 0
2 / 3 == 0 2 % 3 == 2 2 = 0 * 3 + 2
1 / 3 == 0 1 % 3 == 1 1 = 0 * 3 + 1
0 / 3 == 0 0 % 3 == 0 0 = 0 * 3 + 0
-1 / 3 == -1 -1 % 3 == 2 -1 = -1 * 3 + 2
-2 / 3 == -1 -2 % 3 == 1 -2 = -1 * 3 + 1
и т. д.
--
Если серьезно, то меня больше устраивает, когда результат совпадает с выработанным схемами процессора. Логичное для задачи поведение наверное лучше явно программировать.
– avp Jun 17 '13 at 13:14Машино- (да и системно-) зависимые части переносимой программы просто надо выделять в отдельные файлы и использовать условную трансляцию (впрочем, это сильно зависит от языка).
– avp Jun 17 '13 at 17:31#ifdef'ы.) – VladD Jun 17 '13 at 17:41Просто в этом я солидарен с Линусом - C++ is a horrible language.
– avp Jun 17 '13 at 19:41--
По поводу же
C#(мнение Линуса мне неизвестно) - IMHO это просто другой (и похоже хороший) язык, сравнивать его с С++ не стоит. Мне кажется, что основная проблема в нем та же, что и в Java - слишком много классов.(дискутировать о том, хорошо это или плохо, здесь не удастся - лимит комментариев)
--
А вот опасения RMS по поводу шарпа и free software.
– avp Jun 17 '13 at 22:10В гуе вы будете отображать сразу все сотни тысяч строк или воспользуетесь виртуализацией данных в списке, чтобы отображать только несколько десятков видимых элементов? Покрутил ползунок — нагенерировались сотни строчек, отпустил — сборка мусора. Юзер не заметит задержек.
– Kyubey Feb 27 '15 at 17:01