Вопрос по – Почему языки не вызывают ошибки при целочисленном переполнении по умолчанию?

42

В нескольких современных языках программирования (включая C ++, Java и C #) язык позволяетцелочисленное переполнение происходить во время выполнения без возникновения каких-либо условий ошибки.

Например, рассмотрим этот (надуманный) метод C #, который не учитывает возможность переполнения / недостаточного заполнения. (Для краткости способ также не обрабатывает случай, когда указанный список является нулевой ссылкой.)

<code>//Returns the sum of the values in the specified list.
private static int sumList(List<int> list)
{
    int sum = 0;
    foreach (int listItem in list)
    {
        sum += listItem;
    }
    return sum;
}
</code>

Если этот метод вызывается следующим образом:

<code>List<int> list = new List<int>();
list.Add(2000000000);
list.Add(2000000000);
int sum = sumList(list);
</code>

Переполнение произойдет вsumList() метод (потому чтоint в C # введите 32-разрядное целое число со знаком, а сумма значений в списке превышает значение максимального 32-разрядного целого числа со знаком). Переменная sum будет иметь значение -294967296 (не значение 4000000000); скорее всего, это не то, что (гипотетический) разработчик метода sumList намеревался.

Очевидно, что существуют разные методы, которые могут использоваться разработчиками, чтобы избежать возможности целочисленного переполнения, такие как использование типа, такого как Java.BigInteger, илиchecked ключевое слово и/checked переключатель компилятора в C #.

Однако вопрос, который меня интересует, заключается в том, почему эти языки были спроектированы так, чтобы по умолчанию разрешать целочисленные переполнения, а не, например, вызывать исключение, когда во время выполнения выполняется операция, которая может привести к переполнение. Кажется, что такое поведение поможет избежать ошибок в случаях, когда разработчик не учитывает возможность переполнения при написании кода, который выполняет арифметическую операцию, которая может привести к переполнению. (Эти языки могли включать что-то вроде ключевого слова «unchecked», которое может обозначать блок, в котором допускается целочисленное переполнение без возникновения исключения, в тех случаях, когда такое поведение явно задано разработчиком; на самом деле C #есть ли это.)

Ответ просто сводится к производительности - разработчики языка не хотели, чтобы их соответствующие языки по умолчанию имели "медленный" целочисленные арифметические операции, где среде выполнения необходимо будет выполнить дополнительную работу, чтобы проверить, не произошло ли переполнение для каждой применимой арифметической операции - и это соображение производительности перевешивало значение избегания «тихого» вывода; сбои в случае непреднамеренного переполнения?

Существуют ли другие причины для этого решения о дизайне языка, кроме соображений производительности?

Ваш Ответ

8   ответов
26

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

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
7

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

Остальные 1% будут покрывать те случаи, когда люди совершают необычные манипуляции с битами или являются «неточными». в смешивании подписанных и неподписанных операций и хотят семантику переполнения.

8

я математическая операция, которая обычно переводится в одну инструкцию по сборке, должна включать проверку переполнения, что приводит к нескольким инструкциям по сборке, что может привести к тому, что программа будет в несколько раз медленнее.

5

что вы уделяете достаточно внимания размеру ваших типов данных, чтобы в случае переполнения / переполнения это было именно то, что вы хотели. Затем с C ++, C # и Java очень мало изменилось с тем, как & quot; встроенный & quot; типы данных работали.

8

ение на 0 - неопределенное поведение в C ++, а не определенный тип ловушек.

Язык Си не имеет никакой концепции захвата, если только вы не подсчитываете сигналы.

В C ++ есть принцип разработки, согласно которому он не вводит накладные расходы, отсутствующие в C, если вы не попросите об этом. Таким образом, Страуструп не хотел бы предписывать, чтобы целые числа вели себя так, что требует какой-либо явной проверки.

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

Даже если бы C ++ сделал проверку целых чисел, 99% программистов в ранние дни отключили бы для повышения производительности ...

36

бенчмаркинг из коробки.

Когда C # был новым, Microsoft надеялась, что многие разработчики C ++ перейдут на него. Они знали, что многие люди C ++ думали о C ++ как о быстром, особенно быстрее, чем языки, которые "впустую" время на автоматическое управление памятью и тому подобное.

И потенциальные последователи, и рецензенты журналов, вероятно, получат копию нового C #, установят его, создадут тривиальное приложение, которое никто никогда не напишет в реальном мире, запустят его в тесном цикле и измерят, сколько времени это займет. Затем они принимают решение для своей компании или публикуют статью, основанную на этом результате.

Тот факт, что их тест показал, что C # медленнее, чем изначально скомпилированный C ++, - это то, что быстро отключает людей из C #. Тот факт, что ваше приложение на C # будет автоматически распознавать переполнение / переполнение, может пропустить. Таким образом, он выключен по умолчанию.

Я думаю, что очевидно, что 99% времени мы хотим / проверяем, чтобы оно было включено. Это неудачный компромисс.

Error: User Rate Limit Exceededreadonly rect foo;Error: User Rate Limit ExceededDoSomething(foo.X, foo.YError: User Rate Limit ExceededfooError: User Rate Limit ExceededXError: User Rate Limit ExceededfooError: User Rate Limit ExceededYError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded/checkedError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
-4

почему ошибки не будут возникать по умолчанию во время выполнения, сводится к наследию желания создавать языки программирования с ACID-подобным поведением. В частности, принцип, что все, что вы кодируете, должен делать (или не кодировать), он будет делать (или не делать). Если вы не кодировали какой-либо обработчик ошибок, то компьютер "примет" благодаря отсутствию обработчика ошибок вы действительно хотите сделать нелепую, подверженную сбоям вещь, о которой вы говорите.

(Кислотная ссылка:http://en.wikipedia.org/wiki/ACID)

Error: User Rate Limit Exceeded
15

что целочисленное переполнение - это всегда нежелательное поведение.

Иногда целочисленное переполнение - желаемое поведение. Один пример, который я видел, представляет собой представление абсолютного значения заголовка в виде числа с фиксированной точкой. С учетом целого без знака 0 равно 0 или 360 градусов, а максимальное 32-разрядное целое число без знака (0xffffffff) является наибольшим значением чуть ниже 360 градусов.

int main()
{
    uint32_t shipsHeadingInDegrees= 0;

    // Rotate by a bunch of degrees
    shipsHeadingInDegrees += 0x80000000; // 180 degrees
    shipsHeadingInDegrees += 0x80000000; // another 180 degrees, overflows 
    shipsHeadingInDegrees += 0x80000000; // another 180 degrees

    // Ships heading now will be 180 degrees
    cout << "Ships Heading Is" << (double(shipsHeadingInDegrees) / double(0xffffffff)) * 360.0 << std::endl;

}

Вероятно, в других ситуациях переполнение допустимо, аналогично этому примеру.

Error: User Rate Limit ExceededdidError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded

Похожие вопросы