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

Почему задана спецификация С# (реализация int.MinValue/-1)?

Выражение int.Minvalue / -1 приводит к определенному поведению в соответствии со спецификацией С#:

7.8.2 Оператор дивизиона

Если левый операнд является наименьшим представимым значением int или long, а правый операнд равен -1, происходит переполнение. В проверенном контексте это вызывает System.ArithmeticException(или его подкласс). В неконтролируемый контекст, определяется в отношении того, является ли Исключение System.ArithmeticException(или его подкласс) или переполнение не сообщается с результирующим значением, равным левый операнд.

Программа тестирования:

var x = int.MinValue;
var y = -1;
Console.WriteLine(unchecked(x / y));

Это выдает OverflowException на .NET 4.5 32bit, но это не обязательно.

Почему спецификация оставляет решение, определяемое результатом? Здесь дело против этого:

  • Инструкция x86 idiv всегда приводит к исключению в этом случае.
  • На других платформах может потребоваться проверка времени выполнения, чтобы подражать этому. Но стоимость этой проверки была бы низкой по сравнению со стоимостью подразделения. Целочисленное деление чрезвычайно дорого (15-30 циклов).
  • Это открывает риски совместимости ( "write once run никуда" ).
  • Презентация разработчика.

Также интересен тот факт, что если x / y является константой компилятора, мы действительно получаем unchecked(int.MinValue / -1) == int.MinValue:

Console.WriteLine(unchecked(int.MinValue / -1)); //-2147483648

Это означает, что x / y может иметь различное поведение в зависимости от используемой синтаксической формы (и не только в зависимости от значений x и y). Это допускается спецификацией, но это кажется неразумным выбором. Почему С# был создан так?

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

4b9b3361

Ответ 1

Это побочный эффект более крупного брата С# Language Specification, Ecma-335, спецификации инфраструктуры общего языка. Раздел III, глава 3.31 описывает, что делает код операции DIV. Специфика того, что спецификация С# очень часто приходится откладывать, довольно неизбежно. Он указывает, что он может бросать, но не требует этого.

Иначе реалистичная оценка того, что делают настоящие процессоры. И тот, который все используют, является странным. Процессоры Intel чрезмерно причудливы по поводу поведения переполнения, они были разработаны еще в 1970-х годах с предположением, что каждый будет использовать инструкцию INTO. Никто не делает, рассказ на другой день. Однако он не игнорирует переполнение на IDIV и поднимает ловушку #DE, не может игнорировать этот громкий удар.

Довольно сложно написать спецификацию языка поверх спецификации woolly runtime поверх непоследовательного поведения процессора. Мало того, что команда С# могла бы с этим справиться, но продвигать неточный язык. Они уже вышли за рамки спецификации, задокументировав OverflowException вместо ArithmeticException. Очень непослушный. У них был быстрый взгляд.

Взгляд, который показал практику. Очень маловероятно, что проблема будет проблемой, дрожание решает, следует ли встраивать. И не-встроенная версия бросает, ожидание заключается в том, что и встроенная версия. Никто еще не был разочарован.

Ответ 2

Основная цель дизайна С# - "Закон минимального сюрприза". Согласно этому руководству компилятор не должен пытаться угадать намерение программиста, а скорее должен сообщить программисту, что для правильного указания намерения необходимо дополнительное руководство. Это относится к интересующему случаю, потому что в рамках ограничений арифметики с двумя дополнениями результат приводит к очень неожиданному результату: Int32.MinValue/-1 оценивается в Int32.MinValue. Произошло переполнение, и для правильного представления правильного значения Int32.MaxValue + 1 потребуется не доступный 33-й бит, равный 0,

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