Просто добавьте еще одну альтернативу, чтобы избежать условного выполнения (которое я бы не использовал, но, казалось, отсутствует в наборе решений):
int max( int a, int b, int c ) {
int l1[] = { a, b };
int l2[] = { l1[ a<b ], c };
return l2[ l2[0] < c ];
}
Подход использует (как и большинство других) тот факт, что результат логического выражения при преобразовании в int дает либо 0, либо 1. Упрощенная версия для двух значений будет иметь вид:
int max( int a, int b ) {
int lookup[] { a, b };
return lookup[ a < b ];
}
Если выражение a<b
верное, мы возвращаем b
, тщательно сохраненные в первом индексе поискового массива. Если выражение возвращает false, то мы возвращаем a
, который хранится как элемент 0
массива поиска. Используя это как строительный блок, вы можете сказать:
int max( int a, int b, int c ) {
int lookup[ max(a,b), c ];
return lookup[ max(a,b) < c ];
}
Что можно тривиально преобразовать в приведенный выше код, избегая второго вызова внутреннего max
, используя результат, уже сохраненный в lookup[0]
, и вставляя исходный вызов в max(int,int)
.
(Эта часть является еще одним доказательством того, что вы должны измерить, прежде чем делать выводы, см. Редактирование в конце)
Что бы я на самом деле использовал ... ну, наверное, тот, который @Foo Baa здесь , модифицированный для использования встроенной функции, а не макроса. Следующим вариантом будет либо этот, либо один из @MSN здесь .
Общий знаменатель этих трех решений, которых нет в принятом ответе, состоит в том, что они не только избегают синтаксической конструкции if
или троичного оператора ?:
, но и вообще избегают ветвления и это может повлиять на производительность. предсказатель ветвления в ЦП не может пропустить, когда нет ветвей.
При рассмотрении вопроса о производительности сначала подумайте, а затем подумайте
Я на самом деле реализовал несколько различных опций для двухстороннего максимума и проанализировал сгенерированный код компилятором. Следующие три решения генерируют один и тот же код сборки:
int max( int a, int b ) { if ( a < b ) return b; else return a; }
int max( int a, int b ) { return (a < b? b : a ); }
int max( int a, int b ) {
(void)((a < b) && (a = b));
return a;
}
Что неудивительно, поскольку все три представляют одну и ту же операцию. Интересная часть информации состоит в том, что сгенерированный код не содержит никакой ветви. Реализация проста с инструкцией cmovge
(тест, проведенный с g ++ на платформе Intel x64):
movl %edi, %eax # move a into the return value
cmpl %edi, %esi # compare a and b
cmovge %esi, %eax # if (b>a), move b into the return value
ret
Хитрость заключается в инструкции условного перемещения, которая позволяет избежать любой потенциальной ветви.
Ни одно из других решений не имеет ветвей, но все они переводят на большее количество инструкций процессора, чем любое из этого, что в конце дня убеждает нас в том, что мы всегда должны написать простой код и позволить компилятор оптимизирует его для нас.