Как "x & y" может быть ложным, если оба x и y верны?

Контекст:

Я изучаю С# и занимаюсь на Pex для удовольствия. На сайте вам предстоит повторить реализацию секретного алгоритма, набрав код на сайт и изучив, как входы и выходы различаются между вашей реализацией и секретной реализацией.

Проблема:

В любом случае, я застрял на базовом кодовом дуэли под названием XAndY.

От имени казалось очевидным, что ответ был просто:

public static bool Puzzle(bool x, bool y) 
{
    return x && y;
}

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

Input:

x: true y: true (0x02)

Вывод:

моя реализация: true (0x02)

секретная реализация: false

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

код: Головоломка (true, PexSafeHelpers.ByteToBoolean((байт) 2));

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

return x & y;

Вопросы:

Я думал, что для семантических и коротких замыканий вы должны использовать логический && для сравнения логических значений, но независимо:

  • Означает ли это, что x & y и x && y окончательно выполняют не одинаковые выходы для всех возможных аргументов bool? (или это может быть что-то плохое в Pex?)
  • Означает ли это, что вы можете различать разные значения bool true в С#? Если да, то как?

Ответ 1

В головоломке используется то, что, на мой взгляд, является ошибкой в ​​компиляторе С#. (Ошибка также влияет на VB.NET.)

В спецификации С# 5.0 в § 4.1.1 говорится, что "Возможные значения типа bool равны true и false", а в §7.11.3 говорится, что operator &(bool x, bool y) является <сильным > логическим:

Результат x & y равен true, если оба x и y равны true. В противном случае результатом будет false.

Очевидно, что нарушение спецификации для true & true дает false. Что происходит?

Во время выполнения a bool представляется 1-байтовым целым числом. Компилятор С# использует 0 для представления false и 1 для представления true. Чтобы реализовать оператор &, компилятор С# испускает команду побитовой AND в сгенерированном ИЛ. На первый взгляд это выглядит нормально: побитовые операции AND с участием 0 и 1 точно соответствуют логическим операциям AND с false и true.

Однако в §III.1.1.2 спецификации CLI явно разрешено bool представлять целое число, отличное от 0 или 1:

Булевой тип CLI занимает 1 байт в памяти. Битовая диаграмма всех нулей обозначает значение false. Битовая диаграмма с любым одним или несколькими установленными битами (аналогично ненулевому целому числу) обозначает значение true.

Выйдя за рамки С#, действительно возможно - и совершенно законно - создать bool, значение которого, скажем, 2, что приводит к тому, что & будет вести себя неожиданно. Это то, что делает сайт Pex.

Здесь демонстрация:

using System;
using System.Reflection.Emit;

class Program
{
    static void Main()
    {
        DynamicMethod method =
            new DynamicMethod("ByteToBoolean", typeof(bool), new[] { typeof(byte) });
        ILGenerator il = method.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0); // Load the byte argument...
        il.Emit(OpCodes.Ret);     // and "cast" it directly to bool.
        var byteToBoolean =
            (Func<byte, bool>)method.CreateDelegate(typeof(Func<byte, bool>));

        bool x = true;
        bool y = byteToBoolean(2);
        Console.WriteLine(x);               // True
        Console.WriteLine(y);               // True
        Console.WriteLine(x && y);          // True
        Console.WriteLine(x & y);           // False (!) because 1 & 2 == 0
        Console.WriteLine(y.Equals(false)); // False
        Console.WriteLine(y.Equals(true));  // False (!) because 2 != 1
    }
}

Итак, ответы на ваши вопросы:

  • В настоящее время возможно для x & y и x && y иметь разные значения. Однако это поведение нарушает спецификацию С#.
  • В настоящее время вы можете использовать Boolean.Equals (как показано выше), чтобы различать значения true. Однако это поведение нарушает спецификацию CLI Boolean.Equals.

Ответ 2

& в С# не является побитовым оператором, считая, что входные значения Boolean. Он перегружен. Существует два совершенно отдельных реализации оператора. Логический логический оператор, не содержащий коротких замыканий, если входы являются логическими и побитовыми И, если значения являются небулевыми значениями.

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

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

Если операнды могут быть другого типа, кроме bool, то его довольно тривиально, чтобы предоставить тип, который имеет разные результаты для любого оператора, например суперсредний тип, который переопределяет оператор true в усадьбе, несовместимой с это неявное преобразование в bool: