Вы бы использовали num% 2 или num & 1, чтобы проверить, является ли число четным?

Вы можете использовать аннотацию @Rule с ExpectedException , например:

@Rule
public ExpectedException expectedEx = ExpectedException.none();

@Test
public void shouldThrowRuntimeExceptionWhenEmployeeIDisNull() throws Exception {
    expectedEx.expect(RuntimeException.class);
    expectedEx.expectMessage("Employee ID is null");
    // do something that should throw the exception...
}

Обратите внимание, что пример в ExpectedException docs (в настоящее время) неправильный - нет никакого открытого конструктора, поэтому вы должны использовать ExpectedException.none().

20
задан Jon Seigel 8 July 2010 в 19:04
поделиться

11 ответов

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

Итак, что вы хотите - код, который работает медленно на «некоторых компиляторах», или код, который неправильный на «некоторых компиляторах» "? Не обязательно одинаковые компиляторы в каждом случае, но оба вида чрезвычайно редки.

Конечно, если num имеет беззнаковый тип или одно из целых чисел фиксированной ширины C99 типы ( int8_t и т. д., которые должны быть дополнением до 2), то это не проблема. В этом случае я считаю % 2 более элегантным и значимым, и & 1 - это взлом, который, возможно, иногда может быть необходим для производительности. Я думаю, например, что CPython не выполняет эту оптимизацию, и то же самое будет верно для полностью интерпретируемых языков (хотя тогда накладные расходы на синтаксический анализ, вероятно, затмевают разницу между двумя машинными инструкциями). Я был бы немного удивлен, если бы встретил компилятор C или C ++, который не делал этого там, где это было возможно, потому что это несложно в момент выдачи инструкций, если не раньше.

В общем, я бы стал говорят, что в C ++ вы полностью зависите от способности компилятора оптимизировать. Стандартные контейнеры и алгоритмы имеют n уровней косвенного обращения, большинство из которых исчезает, когда компилятор завершает встраивание и оптимизацию.

24
ответ дан 17 October 2019 в 02:38
поделиться

Я не думаю, что модуль делает вещи более читаемыми. Оба имеют смысл, и обе версии верны. Компьютеры хранят числа в двоичном формате, так что вы можете просто использовать двоичную версию.

Компилятор может заменить модульную версию эффективной версией. Но это звучит как оправдание для предпочтения по модулю.

И удобочитаемость в этом очень частном случае одинакова для обеих версий. Читатель, который плохо знаком с программированием, может даже не знать, что вы можете использовать модуль 2 для определения четности числа. Читатель должен это вывести. Он может даже не знать оператора по модулю !

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

if( ( num & 1 ) == 0 ) { /* even */ }
if( ( 00010111b & 1 ) == 0 ) { /* even */ }
if( ( 00010110b & 1 ) == 0 ) { /* odd */ }

(Я использовал суффикс «b» для пояснения только, это не C / C ++)

В версии по модулю вам необходимо дважды проверить, как операция определена в деталях (например, проверьте документацию, чтобы убедиться, что 0% 2 - это то, что вы ожидаете).

Двоичный код И равен проще и без двусмысленностей!

Только приоритет оператора может быть ловушкой для бинарных операторов. Но это не должно быть поводом избегать их (когда-нибудь они все равно понадобятся даже новым программистам).

0
ответ дан 17 October 2019 в 02:38
поделиться

Оба подхода не очевидны, особенно для тех, кто плохо знаком с программированием. Вы должны определить встроенную функцию с описательным именем. Подход, который вы используете в нем, не имеет значения (микрооптимизации, скорее всего, не сделают вашу программу заметно быстрее).

В любом случае, я считаю, что 2) намного быстрее, так как не требует разделения.

0
ответ дан 17 October 2019 в 02:38
поделиться

Оба они довольно интуитивно понятны.

Я бы дал небольшое преимущество num% 2 == 0 , но у меня нет предпочтений . Конечно, что касается производительности, это, вероятно, микрооптимизация, поэтому я бы не стал об этом беспокоиться.

3
ответ дан 17 October 2019 в 02:38
поделиться

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

Я бы сказал, что использование модуля по модулю упростит понимание, но создание is_even Функция , использующая метод x & 1 , дает вам лучшее из обоих миров.

3
ответ дан 17 October 2019 в 02:38
поделиться

Ваш вывод о производительности основан на популярной ложной предпосылке.

По какой-то причине вы настаиваете на переводе языковых операций в их «очевидные» машинные аналоги и делаете выводы о производительности на основе этого перевода. В этом конкретном случае вы пришли к выводу, что побитовая операция и & языка C ++ должна быть реализована машинной операцией побитовой и , а операция по модулю % надо как-то задействовать машину подразделения , которая якобы медленнее. Такой подход имеет очень ограниченное применение, если вообще используется.

Во-первых, я не могу представить себе реально существующий компилятор C ++, который интерпретировал бы языковые операции таким «буквальным» образом, то есть отображая их в «эквивалент». машинные операции. В основном потому, что чаще, чем можно было бы подумать, эквивалентных машинных операций просто не существует.

Когда дело доходит до таких основных операций с непосредственной константой в качестве операнда, любой уважающий себя компилятор всегда немедленно «поймет», что оба num & 1 и num% 2 для целого num делают одно и то же, что заставляет компилятор генерировать абсолютно идентичный код для обоих выражений. Естественно, производительность будет точно такой же.

Кстати, это не называется "оптимизацией". Оптимизация, по определению, - это когда компилятор решает отклониться от стандартного поведения абстрактной машины C ++, чтобы сгенерировать более эффективный код (сохраняя наблюдаемое поведение программы). В этом случае отклонений нет, что означает отсутствие оптимизации.

Более того, вполне возможно, что на данной машине наиболее оптимальным способом реализации обоих является не побитовое и , ни деление , а какой-то другой специальная машинно-ориентированная инструкция. Вдобавок ко всему, вполне возможно, что в какой-либо инструкции вообще не будет необходимости, поскольку четность / нечетность определенного значения может быть выставлена ​​«бесплатно» через флаги состояния процессора или что-то вроде это.

Другими словами, аргумент эффективности неверен.

Во-вторых, возвращаясь к исходному вопросу, более предпочтительным способом определения четности / нечетности значения, безусловно, является num% 2 подход, поскольку он реализует требуемую проверку буквально ("по определению"), и ясно выражает тот факт, что проверка является чисто математической. Т.е. это дает понять, что нас волнует свойство числа , а не свойство его представления (как было бы в случае варианта num & 1

Вариант num & 1 следует зарезервировать для ситуаций, когда вам нужен доступ к битам представления значения числа. Использование этого кода для проверки четности / нечетности является весьма сомнительной практикой.

Вариант 1 следует зарезервировать для ситуаций, когда требуется доступ к битам представления значения числа. Использование этого кода для проверки на четность / нечетность является весьма сомнительной практикой.

Вариант 1 следует зарезервировать для ситуаций, когда требуется доступ к битам представления значения числа. Использование этого кода для проверки на четность / нечетность является весьма сомнительной практикой.

12
ответ дан 17 October 2019 в 02:38
поделиться

Я определяю и использую функцию «IsEven», поэтому мне не нужно думать об этом, затем я выбрал тот или иной метод и забыл, как я проверяю, является ли что-то четным.

Только придирка / предостережение - я бы просто сказал, что с побитовой операцией вы предполагаете что-то о представлении чисел в двоичном формате, а по модулю это не так. Вы интерпретируете число как десятичное значение. Это практически гарантированно работает с целыми числами. Однако учтите, что по модулю будет работать для числа double, а вот побитовая операция - нет.

12
ответ дан 17 October 2019 в 02:38
поделиться

Все зависит от контекста. Я вообще-то предпочитаю подход & 1, если это системный контекст низкого уровня. Во многих из этих контекстов «даже» в основном означает, что для меня имеет младший бит ноль, а не делится на два.

ОДНАКО: ваш единственный лайнер содержит ошибку.

Вы должны идти

if( (x&1) == 0 )

не

if( x&1 == 0 )

Последний выполняет И x с 1 == 0, т. Е. Он выполняет И x с 0, что дает 0, что, конечно, всегда считается ложным.

Итак, если вы сделали именно так, как вы предлагаете, все числа будут нечетными!

7
ответ дан 17 October 2019 в 02:38
поделиться

Сначала я кодирую код для удобства чтения, поэтому мой выбор здесь num% 2 == 0 . Это гораздо яснее, чем num & 1 == 0 . Я позволю компилятору позаботиться об оптимизации за меня и буду настраивать его только в том случае, если профилирование покажет, что это узкое место. Все остальное преждевременно.

Я считаю второй вариант гораздо более элегантным и значимым

Я категорически не согласен с этим. Число даже потому, что его соответствие по модулю два равно нулю, а не потому, что его двоичное представление заканчивается определенным битом. Бинарные представления - это деталь реализации. Опора на детали реализации - это обычно запах кода. Как отмечали другие, тестирование LSB не удается на машинах, которые используют представления дополнения единиц.

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

Я не согласен. Мы все должны писать код, чтобы наши намерения были яснее. Если мы проверяем на равномерность, код должен это выражать (и в комментариях не должно быть необходимости). Опять же, проверка соответствия по модулю два более четко выражает цель кода, чем проверка LSB.

И, что более важно, детали должны быть скрыты в методе isEven . Итак, мы должны увидеть if (isEven (someNumber)) {// details} и увидеть num% 2 == 0 только один раз в определении isEven .

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

Я не согласен. Мы все должны писать код, чтобы наши намерения были яснее. Если мы проверяем на равномерность, код должен это выражать (и в комментариях не должно быть необходимости). Опять же, проверка соответствия по модулю два более четко выражает цель кода, чем проверка LSB.

И, что более важно, детали должны быть скрыты в методе isEven . Таким образом, мы должны увидеть if (isEven (someNumber)) {// details} и увидеть num% 2 == 0 только один раз в определении isEven .

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

Я не согласен. Мы все должны писать код, чтобы наши намерения были яснее. Если мы проверяем на равномерность, код должен это выражать (и в комментариях не должно быть необходимости). Опять же, проверка соответствия по модулю два более четко выражает цель кода, чем проверка LSB.

И, что более важно, детали должны быть скрыты в методе isEven . Итак, мы должны увидеть if (isEven (someNumber)) {// details} и увидеть num% 2 == 0 только один раз в определении isEven .

Мы все должны писать код, чтобы наши намерения были яснее. Если мы тестируем на равномерность, код должен это выражать (и в комментариях не должно быть необходимости). Опять же, проверка соответствия по модулю два более четко выражает цель кода, чем проверка LSB.

И, что более важно, детали должны быть скрыты в методе isEven . Итак, мы должны увидеть if (isEven (someNumber)) {// details} и увидеть num% 2 == 0 только один раз в определении isEven .

Мы все должны писать код, чтобы наши намерения были яснее. Если мы проверяем на равномерность, код должен это выражать (и в комментариях не должно быть необходимости). Опять же, проверка соответствия по модулю два более четко выражает цель кода, чем проверка LSB.

И, что более важно, детали должны быть скрыты в методе isEven . Итак, мы должны увидеть if (isEven (someNumber)) {// details} и увидеть num% 2 == 0 только один раз в определении isEven .

детали должны быть скрыты в методе isEven . Таким образом, мы должны увидеть if (isEven (someNumber)) {// details} и увидеть num% 2 == 0 только один раз в определении isEven .

детали должны быть скрыты в методе isEven . Таким образом, мы должны увидеть if (isEven (someNumber)) {// details} и увидеть num% 2 == 0 только один раз в определении isEven .

76
ответ дан 17 October 2019 в 02:38
поделиться

На данный момент, может быть, я просто добавляю шум, но что касается читабельности, то опция modulo имеет больший смысл. Если ваш код не читабельный, то он практически бесполезен.

Также, если это не код для запуска на системе, которая действительно перегружена ресурсами (думаю, микроконтроллер), не пытайтесь оптимизировать для оптимизатора компилятора.

.
-2
ответ дан 17 October 2019 в 02:38
поделиться

Неоднократно упоминалось, что любой современный компилятор создаст одну и ту же сборку для обоих вариантов. Это напомнило мне демо-страницу LLVM, которую я видел где-то на днях, так что я решил попробовать. Я знаю, что это не намного больше, чем анекдот, но это подтверждает то, что мы ожидали: x%2 и x&1 реализованы идентично.

Я также пытался скомпилировать оба эти варианта с gcc-4.2.1 (gcc -S foo.c), и получившаяся сборка идентична (и вставлена в нижнюю часть этого ответа).

Запрограммируйте первое:

int main(int argc, char **argv) {
  return (argc%2==0) ? 0 : 1;
}

Результат:

; ModuleID = '/tmp/webcompile/_27244_0.bc'
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32"
target triple = "i386-pc-linux-gnu"

define i32 @main(i32 %argc, i8** nocapture %argv) nounwind readnone {
entry:
    %0 = and i32 %argc, 1       ; <i32> [#uses=1]
    ret i32 %0
}

Запрограммируйте второе:

int main(int argc, char **argv) {
  return ((argc&1)==0) ? 0 : 1;
}

Результат:

; ModuleID = '/tmp/webcompile/_27375_0.bc'
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32"
target triple = "i386-pc-linux-gnu"

define i32 @main(i32 %argc, i8** nocapture %argv) nounwind readnone {
entry:
    %0 = and i32 %argc, 1       ; <i32> [#uses=1]
    ret i32 %0
}

Выход GCC:

.text
.globl _main
_main:
LFB2:
  pushq %rbp
LCFI0:
  movq  %rsp, %rbp
LCFI1:
  movl  %edi, -4(%rbp)
  movq  %rsi, -16(%rbp)
  movl  -4(%rbp), %eax
  andl  $1, %eax
  testl %eax, %eax
  setne %al
  movzbl  %al, %eax
  leave
  ret
LFE2:
  .section __TEXT,__eh_frame,coalesced,no_toc+strip_static_syms+live_support
EH_frame1:
  .set L$set$0,LECIE1-LSCIE1
  .long L$set$0
LSCIE1:
  .long 0x0
  .byte 0x1
  .ascii "zR\0"
  .byte 0x1
  .byte 0x78
  .byte 0x10
  .byte 0x1
  .byte 0x10
  .byte 0xc
  .byte 0x7
  .byte 0x8
  .byte 0x90
  .byte 0x1
  .align 3
LECIE1:
.globl _main.eh
_main.eh:
LSFDE1:
  .set L$set$1,LEFDE1-LASFDE1
  .long L$set$1
ASFDE1:
  .long LASFDE1-EH_frame1
  .quad LFB2-.
  .set L$set$2,LFE2-LFB2
  .quad L$set$2
  .byte 0x0
  .byte 0x4
  .set L$set$3,LCFI0-LFB2
  .long L$set$3
  .byte 0xe
  .byte 0x10
  .byte 0x86
  .byte 0x2
  .byte 0x4
  .set L$set$4,LCFI1-LCFI0
  .long L$set$4
  .byte 0xd
  .byte 0x6
  .align 3
LEFDE1:
  .subsections_via_symbols
9
ответ дан 17 October 2019 в 02:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: