Как лучше всего в C узнать, делится ли одно число на другое?

Как лучше всего в C узнать, делится ли одно число на другое? Я использую это:

if (!(a % x)) {
// this will be executed if a is divisible by x
}

Есть ли способ быстрее? Я знаю, что выполнение, то есть 130% 13, приведет к выполнению 130/13 из 10 раз. Итак, есть 10 циклов, когда нужен только один (я просто хочу знать, делится ли 130 на 13).

Спасибо!


person Joseph    schedule 25.05.2011    source источник
comment
Вы этого не знаете, хотя можете ошибаться. На современном процессоре оператор мода обычно представляет собой инструкцию с одним машинным кодом.   -  person    schedule 25.05.2011
comment
130% 13 приведет к тому, что вы сделаете 130/13 из 10 раз - с чего вы так думаете?   -  person Michael Burr    schedule 25.05.2011
comment
И еще раз, кто, черт возьми, за это голосует?   -  person    schedule 25.05.2011
comment
Использование !(a % b) - плохой стиль, потому что вы имеете дело с числами. Лучше используйте a % b == 0.   -  person starblue    schedule 25.05.2011
comment
Голосование закрыто, тоже локализовано.   -  person user7116    schedule 26.05.2011
comment
Использование ! с числами - неплохой стиль. Это C, а не Java ...   -  person R.. GitHub STOP HELPING ICE    schedule 26.05.2011


Ответы (5)


Я знаю, что выполнение, то есть 130% 13 приведет к выполнению 130/13 за 10 раз

Балдердаш. % не делает ничего подобного ни на одном из процессоров, которые я когда-либо использовал. Он выполняет 130/13 один раз и возвращает остаток.

Используйте %. Если ваше приложение работает слишком медленно, профилируйте его и исправьте все, что работает слишком медленно.

person Robᵩ    schedule 25.05.2011

Для двух произвольных чисел лучший способ проверить, есть ли a % b == 0. Оператор модуля имеет разную производительность в зависимости от оборудования, но ваш компилятор может понять это намного лучше, чем вы. Оператор модуля универсален, и ваш компилятор определит наилучшую последовательность инструкций для любого оборудования, на котором вы работаете.

Если одно из чисел является константой, ваш компилятор может оптимизировать, выполнив некоторую комбинацию битовых сдвигов и вычитаний (в основном для степеней двойки), поскольку аппаратный модуль div / mod работает медленнее, чем сложение или вычитание, но на современных процессорах задержка (уже всего несколько наносекунд) скрыто множеством других уловок производительности, поэтому вам не нужно об этом беспокоиться. Никакое аппаратное обеспечение не вычисляет модуль путем повторного деления (некоторые старые процессоры делали деление посредством повторяющихся битовых сдвигов и вычитания, но они по-прежнему использовали для этого специализированное оборудование, так что аппаратное обеспечение еще быстрее, чем пытаться эмулировать это в программном обеспечении). Большинство современных ISA фактически вычисляют как деление, так и остаток в одной инструкции.

Единственная оптимизация, которая может быть полезной, - это когда делитель равен степени двойки. Затем вы можете использовать &, чтобы замаскировать младшие биты (с помощью делителя - 1) и проверить результат на ноль. Например, чтобы проверить, делится ли a на 8, a & 7 == 0 эквивалентно. Хороший компилятор сделает это за вас, так что придерживайтесь только %.

person Hoa Long Tam    schedule 25.05.2011

В общем случае использование оператора по модулю, вероятно, будет самым быстрым доступным методом. Есть исключения, особенно если вас интересует, делятся ли числа на степень двойки (в этом случае доступны побитовые операции), но компилятор должен выбирать их автоматически, если вы просто используете %. Вы вряд ли сможете улучшить произвольные значения, такие как 13.

Кроме того, что вы имеете в виду, говоря «делать 130/13 за 10 раз»? Это делает 130 / 13 один раз. Что и требуется.

person bdonlan    schedule 25.05.2011

Если x - константа, тогда да:

if (a * 0x4ec4ec4ec4ec4ec5 < 0x13b13b13b13b13b2) {
    // this will be executed if a is divisible by 13
}

0x4ec4ec4ec4ec4ec5 - это модульное мультипликативное обратное 13 (по модулю 2 64), поэтому, если a действительно кратно 13, тогда произведение будет меньше (2 64 / 13). (Поскольку a в 13 раз больше целого числа n, а n должно соответствовать 64-битному слову, что означает, что оно было меньше 2 64.)

Это работает только для нечетных значений x. Для четных чисел (т. Е. Кратных 2 y для y> 0) вы можете объединить этот тест с тестом побитового И (последние y биты a должны быть равны нулю. Если они равны, разделите a на 2 y и приступайте к проверке умножения.)

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


Изменить: я также предполагаю, что a и x беззнаковые.

person finnw    schedule 25.05.2011
comment
Компиляторы тоже умеют это делать. Взгляните на оптимизированный вывод (x % 13) в GCC или MSVC, и вы увидите, что часть вашего магического числа используется (0x4ec4ec4e + 1), а также нет фактической операции деления. См. stackoverflow.com/questions/2580680/ для получения дополнительных сведений. - person Michael Burr; 26.05.2011
comment
Четкое объяснение этой точной идеи: math.stackexchange.com/questions/1251327/ - person allonhadaya; 16.12.2016

Когда машина выполняет %, она просто выполняет инструкцию деления, которая автоматически генерирует остаток.

Однако имейте в виду, что если одно из чисел отрицательное, % даст отрицательный остаток. Если вас интересует только остаток от нуля, это не проблема, но если вы ищете другой остаток, например 1, это действительно может сбить вас с толку.

person Mike Dunlavey    schedule 25.05.2011
comment
Хорошая точка зрения! Но они оба всегда позитивны. Спасибо, в любом случае :( - person Joseph; 28.05.2011