Переполнение после неявного приведения

Когда я пытаюсь сделать неявное приведение от двойного к беззнаковому длинному, у меня появляется предупреждение о переполнении: «предупреждение: переполнение при неявном преобразовании константы [-Woverflow]».

Вот инструкция:

unsigned long ulongMax = pow(2.0, 64.0) - 1; 

Но когда я явно использую это, как показано ниже, все в порядке!

unsigned long ulongMax = (unsigned long) (pow(2.0, 64.0) - 1);

Я не понимаю, почему у меня есть предупреждение, результат (18446744073709551615) такой же, как ULONG_MAX из заголовка "limits.h".


person Burrich    schedule 26.11.2015    source источник
comment
Ваш вопрос должен включать точное предупреждающее сообщение, и было бы хорошо подтвердить размер ulong, например. printf("%lu\n", ULONG_MAX);   -  person M.M    schedule 27.11.2015
comment
Я включил предупреждающее сообщение. В моей системе (64-битная версия Arch Linux) значение ULONG_MAX равно 18446744073709551615, как я уже сказал.   -  person Burrich    schedule 27.11.2015


Ответы (3)


pow(2.0, 64.0) возвращает double.

Однако (при условии нормальной системы IEEE754) значения pow(2.0, 64.0) и pow(2.0, 64.0) - 1 на самом деле равны. Это потому, что мы находимся за пределами диапазона, в котором соседние целые числа точно представляются в double. (Конечно, 64-битный двойник не может представлять все 64-битные целые числа).

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

Ваш компилятор пытается быть полезным, предупреждая вас об этом неопределенном поведении в первом случае, но (предположительно) он рассматривает добавление приведения как ваше сообщение: «Я не хочу слышать это предупреждение».

person M.M    schedule 27.11.2015
comment
На мой взгляд, двойной был представлен как int, но да, вы правы. Таким образом, максимальное значение целого числа внутри двойного числа должно быть 2 ^ 52 -1. Спасибо. - person Burrich; 27.11.2015

предупреждение означает, что потенциально может быть потеряна точность, если pow(2.0, 64.0) - 1 будет слишком большим или с дробной частью (скажем, 1,7 * 10 ^ 308, что является максимальным значением для удвоения) или 0,9, что будет усечено до 0).

Причина, по которой вы не получаете предупреждение при использовании явного приведения (unsigned long) (pow(2.0, 64.0) - 1), заключается в том, что, будучи явным, вы говорите: «Я действительно хочу получить unsigned long от этого двойника (независимо от неприятных последствий этого)»

person Roberto    schedule 26.11.2015

Наиболее вероятная причина, по которой вы получаете предупреждение, заключается в том, что unsigned long на вашей платформе имеет 32-разрядный, а не 64-разрядный размер.

Если вы переключитесь на 64-битный беззнаковый, предупреждение исчезнет:

unsigned long long ulongMax = pow(2.0, 64.0) - 1; 
printf("%llu", ulongMax);

Демонстрация

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

person Sergey Kalinichenko    schedule 26.11.2015