как компиляторы обнаруживают переполнение чисел при компиляции? - PullRequest
3 голосов
/ 02 июня 2011

Компилятор работает с исходным кодом в виде строк, например, в C ++, например, когда он поощряет оператор типа unsigned char x = 150;, он знает из пределов типа, что unsigned char должен находиться в диапазоне от 0 до 255.

У меня вопрос, пока число 150 остается строкой, какой компилятор алгоритма использует для сравнения последовательности цифр - 150 в данном случае - с ограничениями типов?

Я сделал простой алгоритм, чтобы сделать это для типа 'int'для десятичного, восьмеричного, шестнадцатеричного и двоичного числа с прямым порядком байтов, но я не думаю, что компилятор делает такую ​​вещь, чтобы обнаружить переполнение в числах.

алгоритм, который я сделал, написан на C ++:

typedef signed char int8;
typedef signed int  int32;

#define DEC  0
#define HEX  1
#define OCT  2
#define BIN  3

bool isOverflow(const char* value, int32 base)
{
    // left-most digit for maximum and minimum number
    static const char* max_numbers[4][2] =
    {
        //                 INT_MAX                           INT_MIN
        {                       "2147483647",                       "2147483648" }, // decimal
        {                         "7fffffff",                         "80000000" }, // hexadecimal
        {                      "17777777777",                      "20000000000" }, // octal
        { "01111111111111111111111111111111", "10000000000000000000000000000000" }  // binary
    };

    // size of strings in max_numbers array
    static const int32 number_sizes[] = { 10, 8, 11, 32 };

    // input string size
    int32 str_len = strlen(value);

    // is sign mark exist in input string
    int32 signExist = ((base == DEC || base == OCT) && *value == '-');

    // first non zero digit in input number
    int32 non_zero_index = signExist;

    // locate first non zero index
    while(non_zero_index < str_len && value[non_zero_index] == 0) non_zero_index++;

    // if non_zero_index equal length then all digits are zero
    if (non_zero_index == str_len) return false;

    // get number of digits that actually represent the number
    int32 diff = str_len - non_zero_index;

    // if difference less than 10 digits then no overflow will happened
    if (diff < number_sizes[base]) return false;
    // if difference greater than 10 digits then overflow will happened
    if (diff > number_sizes[base]) return true;

    // left digit in input and search strings
    int8 left1 = 0, left2 = 0;

    // if digits equal to 10 then loop over digits from left to right and compare
    for (int32 i = 0; non_zero_index < str_len; non_zero_index++, i++)
    {
        // get input digit
        left1 = value[non_zero_index];
        // get match digit
        left2 = max_numbers[signExist][i];

        // if digits not equal then if left1 is greater overflow will occurred, false otherwise
        if (left1 != left2) return left1 > left2;
    }

    // overflow won't happened
    return false;
}

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

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

Ответы [ 5 ]

6 голосов
/ 02 июня 2011

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

Но теперь рассмотрим исходную проблему;а что если вы взяли цифры и просто создали подпрограммы для обработки их как чисел?Скажем, например, алгоритм, который может принять

6 + 5

и вычислить сумму в виде двузначной строки 11?Распространите это на другие операции, и вы сможете вычислить, больше ли 32769, чем 32768 напрямую.

1 голос
/ 02 июня 2011

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

Я не могу себе представить, почему этобыло бы лучше сравнить строки.

Для поплавков проблема сложнее из-за точности и округления.

0 голосов
/ 02 июня 2011

В большинстве теорий компилятора текст программы ( единица перевода ) конвертируется в токены и значения .Например, текст «150» будет преобразован в токен постоянное целое число со значением 150. Это, конечно, после запуска препроцессора.

Затем компилятор начинает процесс синтаксической и семантической проверки.Таким образом, оператор присваивания оценивается на синтаксис (правильное написание и формат), а затем проверяется на семантику.

Компилятор может либо пожаловаться на значение, которое выходит за пределы допустимого диапазона (например, -150 для unsigned char), либо применить некоторые преобразования.В случае -150 это значение будет преобразовано в 8-битное значение (наиболее значимый бит, который указывает на отрицательность, теперь равен 128).Я не адвокат по языку, поэтому я точно не знаю, какую свободу имеет компилятор в этом отношении, а также требуется ли предупреждение или нет.

Таким образом, у компилятора есть некоторые свободы при оценке операторов и проверке семантики.Весь текст преобразуется во внутреннее представление для токенов и значений (более компактная структура данных).Проверка того, находится ли константный целочисленный литерал в пределах диапазона для оператора присваивания, выполняется на этапе семантики процесса компиляции.Семантика определяется из языкового стандарта или политики компании.Некоторая семантика превращена в параметры компилятора и оставлена ​​для программиста.

0 голосов
/ 02 июня 2011

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

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

Скорее всего, я думаю, компиляторы преобразуют в целочисленные типы непосредственно в семантических действиях своего (высоко оптимизированного) синтаксического анализатора.

0 голосов
/ 02 июня 2011

Я не уверен, какие именно алгоритмы для этого используют большинство компиляторов, но вот несколько вариантов, которые могут работать:

  1. Компилятор может попытаться использовать существующую библиотеку (например, в C ++, stringstream), чтобы попытаться преобразовать строку в число соответствующего типа. Затем его можно использовать для проверки ошибок.

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

...