Подтвердить что ты не робот

Сделать целое число четным

Иногда мне нужно быть уверенным, что некоторое целое число равно. Поэтому я мог бы использовать следующий код:

int number = /* magic initialization here */;

// make sure the number is even
if ( number % 2 != 0 ) {
    number--;
}

но это не похоже на очень эффективный самый эффективный способ сделать это, поэтому я мог бы сделать следующее:

int number = /* magic initialization here */;

// make sure the number is even
number &= ~1;

но (к тому же, не будучи читаемым), я не уверен, что решение полностью переносимо.

  • Какое решение вы считаете лучшим?
  • Является ли второе решение полностью переносимым?
  • Является ли второе решение значительно быстрее, чем первое?
  • Какие еще решения вы знаете по этой проблеме?
  • Что делать, если я делаю это внутри встроенного метода? Он должен (теоретически) быть таким же быстрым, как эти решения, и читаемость больше не должна быть проблемой, делает ли это второе решение более жизнеспособным?

note: этот код должен работать только с положительными целыми числами, но с решением, которое также работает с отрицательными числами, будет плюсом.

4b9b3361

Ответ 1

int even_number = (number / 2) * 2;

Это должно работать независимо от архитектуры, пока оптимизатор не собирается мешать (он не должен, но кто знает).

Ответ 2

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

inline int make_even(int n)
{
    return n - n % 2;
}

// ....

int m = make_even(n);

Ответ 3

Я бы использовал второе решение. В любом двоичном представлении, независимо от количества бит, бит-триана или мало-северного или других различий в архитектуре, эта операция приведет к установке минимального бита в ноль. Он быстрый и полностью портативный. Цель кода может быть объяснена с помощью комментариев, если вы встретите нищих программистов С, которые не могут понять, что это значит.

Ответ 4

Прежде чем принять ответ, я сделаю свой собственный, который попытается обобщить и заполните некоторую информацию, найденную здесь:

Четыре возможных метода, которые описаны (и некоторые небольшие их варианты).

  • if (number % 2 != 0) { number--; }
  • number&= ~1
  • number = number - (number % 2);
  • number = (number / 2) * 2;

Прежде чем продолжить, позвольте мне уточнить: Ожидаемый выигрыш для использования любого из этих методов минимален, даже если бы мы могли доказать, что один метод на 200% быстрее, чем другие, худший из них настолько быстр что единственный способ получить видимый выигрыш в скорости будет, если бы этот метод был многократно вызываемых в приложении с привязкой к процессору. Таким образом, это больше упражнения для удовольствия, чем настоящая оптимизация.

Анализ

читаемость

Что касается читаемости, я бы оценил метод 1 как наиболее читаемый, метод 4 как второй лучший, а метод 2 - хуже. Люди могут не согласиться, но я оценил их так, потому что:

  • В методе 1 максимально ясно, что если число нечетное, вы хотите вычесть из него, делая это даже.
  • Метод 4 также очень ясен, но я занял второе место, потому что первый взгляд, вы можете подумать, что он ничего не делает, и лишь небольшая часть второй - вы похожи на "О... Целочисленное разделение".
  • Метод 2 и 3 почти эквивалентны с точки зрения удобочитаемости, но многие люди не привыкли к побитовым операциям, и поэтому я оценил метод 2 как тем хуже.

Имея это в виду, я бы добавил, что принято считать, что лучший способ для реализации этого используется функция inline, и ни один из параметров не является эта нечитабельная читаемость не является проблемой (прямое использование в коде ясны и понятны, и чтение метода никогда не будет таким трудным).

Если вы не хотите использовать метод inline, я бы рекомендовал вам использовать метод 1 или метод 4.

Проблемы с совместимостью

Underflow

Было упомянуто, что метод 1 может быть опущен, в зависимости от того, как процессор представляет целые числа. Чтобы убедиться, что вы можете добавить следующее STATIC_ASSERT при использовании метода 1.

STATIC_ASSERT(INT_MIN % 2 == 0, make_even_may_underflow);

Что касается метода 3, даже если INT_MIN даже не может быть недостаточно в зависимости от того, имеет ли результат тот же знак делителя или дивиденды. Наличие одного и того же знака делителя никогда не происходит, потому что INT_MIN - (-1) ближе к 0. Добавьте только STATIC_ASSERT, чтобы убедиться:

STATIC_ASSERT(INT_MIN % 2 == 0 || -1 % 2 < 0, make_even_may_underflow);

Конечно, вы можете использовать эти методы, когда STATIC_ASSERT терпит неудачу с тех пор это будет проблемой только при передаче INT_MIN вашему методу make_even но я бы настоятельно советовал ему.

(Un) поддерживаемые представления бит

При использовании метода 2 вы должны убедиться, что ваше представление битов компилятора ведет себя как ожидалось:

STATIC_ASSERT( (1 & ~1) == 0, unsupported_bit_representation);

// two complement OR sign-and-magnitude.
STATIC_ASSERT( (-3 & ~1) == -4 || (-3 & ~1) == -2 , unsupported_bit_representation); 

Speed ​​

Я также сделал некоторые наивные тесты скорости, используя утилиту Unix time. Я побежал каждый разного метода (и его вариаций) 4 раза и записал результаты, поскольку результаты не сильно менялись, я не счел нужным проводить больше тестов.

Полученные результаты показывают метод 4 и метод 2 как самый быстрый из них все.

Заключение

Согласно предоставленной информации, я бы рекомендовал использовать метод 4. Его читаемый, я не знаю о каких-либо проблемах совместимости и отлично работает.

Надеюсь, вам понравится этот ответ и используйте информацию, содержащуюся здесь, чтобы сделать ваш собственный осознанный выбор.:)


исходный код доступен, если вы хотите проверить мои результаты. пожалуйста, обратите внимание что тесты, скомпилированные с помощью g++ и выполняемые в Mac OS X. Различные платформы и компиляторы могут давать разные результаты.

Ответ 5

Решение &= выглядит лучше всего для меня. Если вы хотите сделать его более портативным и более читаемым:

const int MakeEven = -2;

int number = /* magic initialization here */
// Make sure number is even
number &= MakeEven;

Второе решение должно быть значительно быстрее первого. Он полностью переносится? Скорее всего, хотя, вероятно, есть компьютер где-то, что делает математику иначе.

Это должно работать для целых положительных и отрицательных чисел.

Ответ 6

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

 BOOST_STATIC_ASSERT( (1 & ~1) == 0 );

 BOOST_STATIC_ASSERT( (-1 & ~1) == -2 );

Ответ 7

Ваше второе решение работает только в том случае, если ваше представление знака - "два дополнения" или "знак и величина". Чтобы сделать это на месте, я пошел бы с вариантом сузертерпатта, который должен (почти) всегда работать

number -= (number % 2);

Вы не знаете точно, в каком направлении это будет "круглым" для отрицательных значений, поэтому в крайних случаях у вас может быть недополнение.

Ответ 8

Следующий подход прост и не требует умножения или деления.

number = number & ~1;

или

number = (number + 1) & ~1;