Ответ 1
Он называется Math.Max
:
Math.Max(0, x)
Подобно в концепции для Math.Abs () - Я ищу функцию, которая при задании положительного целого числа вернет одно и то же целое число. Если задано отрицательное значение, он будет возвращать ноль.
Итак:
f(3) = 3
f(0) = 0
f(-3) = 0
Да, это достаточно просто, чтобы писать самостоятельно, но мне интересно, имеет ли этот класс .NET Math этот встроенный интерфейс или может быть достигнут то же самое путем умного соединения нескольких вызовов Math. *
Он называется Math.Max
:
Math.Max(0, x)
Это похоже на то, что вы хотите, нет?
Math.Max(0, num);
Я думаю,
Math.Max(0, x)
- это то, что вы хотите.
Похоже, Math.Max - это путь, но это тоже сработает...;)
(num + Math.Abs(num)) / 2
Math.Max лучше, но без Math и VB
(num >= 0) * -num
Учитывая 32-разрядное целое число со знаком num
, следующее выражение возвращает ноль, если оно отрицательное, или исходное неизмененное значение в противном случае:
(~num >> 31) & num
Эта операция иногда называется фиксированием; значения меньше нуля фиксируются на нуле. Чтобы осуществить зажим на самом num
, используйте следующее утверждение:
num &= ~num >> 31;
Только положительные целые числа (и ноль) имеют 0
для своего знакового бита, который является самым левым или "самым значимым битом" (a.ka., "MSB"), Давайте рассмотрим 32-битный случай. Поскольку позиции битов нумеруются слева направо, начиная с 0, знаковый бит - "бит 31". Перевернув этот бит, а затем распространяя его на каждую из 31 позиции бита, вы получите результат, где либо:
0xFFFFFFFF
, -1
) или0x00000000
, 0
).маскируя исходное значение этим результатом, вы обнуляете значение, но только если оно изначально было отрицательным.
Замечания
Поскольку &
(bitwise-AND
) имеет очень низкий приоритет в С#, вам обычно приходится заключать эти выражения в внешние скобки:
((~num >> 31) & num)
Если num
без знака (например, uint ui
), вы должны использовать приведение, чтобы убедиться, что сдвиг подписан. Это называется вправо-арифметическим сдвигом, и это гарантирует, что MSB дублируется в каждую сдвинутую вправо позицию:
((int)~ui >> 31) & ui
Для 64-битных значений сдвиньте на 63 бита вместо 31:
/* signed */ long v; (~v >> 63) & v
/* unsigned */ ulong ul; ((long)~ul >> 63) & ul
Как показано, вы должны использовать оператор ~
(bitwise-NOT
), чтобы перевернуть бит знака. Если вы попытаетесь использовать вместо этого "унарный минус" -
, вы получите неправильный ответ для значения 0x80000000
, поскольку это одно из двух целочисленных значений, на которое не влияет применение знака минус. bitwise-NOT
, с другой стороны, гарантированно перебрасывает каждый бит любого/каждого значения. (Другое значение, которое нельзя отрицать, равно нулю, что в данной конкретной ситуации в любом случае работает правильно)
Если вы спешите, вот несколько проверенных методов расширения, готовых для копирования/вставки.
public static int Clamp0(this int v) => v & ~v >> 31;
public static long Clamp0(this long v) => v & ~v >> 63;
Одна из опасностей, связанных с использованием методов, показанных в (5.), заключается в том, что нет ошибки или предупреждения, если вызывающая программа забывает назначить возвращаемое значение чему-либо. В С# 7 вы можете определить методы расширения по ссылкам, которые допускают in-situ мутацию типов значений. Такие методы помогают избежать вышеупомянутой проблемы, поскольку они могут (и, соответственно, всегда должны) быть объявлены как возвращающие void
:
public static void RefClamp0(this ref int v) => v &= ~v >> 31;
public static void RefClamp0(this ref long v) => v &= ~v >> 63;
// 'void' ──^ 'ref' ──^ ^── result assigned by callee
Примеры сайтов вызовов для предыдущего метода расширения by-ref в int
:
int x = -999;
x.RefClamp0(); // CORRECT, clamps the value of 'x' in-situ; now x == 0
// x = x.RefClamp0(); // NO -- 'void' return enforces correct by-ref usage
// CS0029: Cannot implicitly convert type 'void' to 'int'
// -999.RefClamp0(); // NO -- compiler errors:
// CS1510: A ref or out value must be an assignable variable
// CS0201: Only [expressions] can be used as a statement
Подробнее о коде без ветвления!
Этот пример кода, приведенный выше, является одним из самых простых примеров с битами, который демонстрирует код без ветвей. Если вы не знакомы с ним, этот термин обычно относится к широкому спектру методов микрооптимизации, которые пытаются минимизировать условные переходы в пользовательском коде, чтобы уменьшить вероятность ошибочных прогнозов в конвейере ЦП.