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

Поиск метода .NET Math, который будет нулевым числом с отрицательным числом

Подобно в концепции для Math.Abs ​​() - Я ищу функцию, которая при задании положительного целого числа вернет одно и то же целое число. Если задано отрицательное значение, он будет возвращать ноль.

Итак:

f(3) = 3
f(0) = 0
f(-3) = 0

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

4b9b3361

Ответ 1

Он называется Math.Max:

Math.Max(0, x)

Ответ 2

Это похоже на то, что вы хотите, нет?

Math.Max(0, num);

Ответ 3

Я думаю,

Math.Max(0, x)

- это то, что вы хотите.

Ответ 4

Похоже, Math.Max ​​- это путь, но это тоже сработает...;)

(num + Math.Abs(num)) / 2

Ответ 5

Math.Max ​​лучше, но без Math и VB

(num >= 0) * -num

Ответ 6

Учитывая 32-разрядное целое число со знаком num, следующее выражение возвращает ноль, если оно отрицательное, или исходное неизмененное значение в противном случае:

          (~num >> 31) & num

Эта операция иногда называется фиксированием; значения меньше нуля фиксируются на нуле. Чтобы осуществить зажим на самом num, используйте следующее утверждение:

          num &= ~num >> 31;



Объяснение

Только положительные целые числа (и ноль) имеют 0 для своего знакового бита, который является самым левым или "самым значимым битом" (a.ka., "MSB"), Давайте рассмотрим 32-битный случай. Поскольку позиции битов нумеруются слева направо, начиная с 0, знаковый бит - "бит 31". Перевернув этот бит, а затем распространяя его на каждую из 31 позиции бита, вы получите результат, где либо:

  • для положительных значений и нуля устанавливаются все биты (0xFFFFFFFF, -1) или
  • для отрицательных значений все биты очищаются (0x00000000, 0).

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

Замечания

  1. Поскольку & (bitwise-AND) имеет очень низкий приоритет в С#, вам обычно приходится заключать эти выражения в внешние скобки:

    ((~num >> 31) & num)
    
  2. Если num без знака (например, uint ui), вы должны использовать приведение, чтобы убедиться, что сдвиг подписан. Это называется вправо-арифметическим сдвигом, и это гарантирует, что MSB дублируется в каждую сдвинутую вправо позицию:

    ((int)~ui >> 31) & ui
    
  3. Для 64-битных значений сдвиньте на 63 бита вместо 31:

    /* signed */     long v;      (~v >> 63) & v
    
    /* unsigned */   ulong ul;    ((long)~ul >> 63) & ul
    
  4. Как показано, вы должны использовать оператор ~ (bitwise-NOT), чтобы перевернуть бит знака. Если вы попытаетесь использовать вместо этого "унарный минус" -, вы получите неправильный ответ для значения 0x80000000, поскольку это одно из двух целочисленных значений, на которое не влияет применение знака минус. bitwise-NOT, с другой стороны, гарантированно перебрасывает каждый бит любого/каждого значения. (Другое значение, которое нельзя отрицать, равно нулю, что в данной конкретной ситуации в любом случае работает правильно)

  5. Если вы спешите, вот несколько проверенных методов расширения, готовых для копирования/вставки.

    public static int Clamp0(this int v) => v & ~v >> 31;
    public static long Clamp0(this long v) => v & ~v >> 63;
    
  6. Одна из опасностей, связанных с использованием методов, показанных в (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
    


Подробнее о коде без ветвления!

Этот пример кода, приведенный выше, является одним из самых простых примеров с битами, который демонстрирует код без ветвей. Если вы не знакомы с ним, этот термин обычно относится к широкому спектру методов микрооптимизации, которые пытаются минимизировать условные переходы в пользовательском коде, чтобы уменьшить вероятность ошибочных прогнозов в конвейере ЦП.