При приведении числа с плавающей точкой к целому числу переполнение вызывает неопределенное поведение. Из спецификации C99, раздел 6.3.1.4 вещественное и целое число :
Когда конечное значение реального плавающего типа преобразуется в целочисленный тип, отличный от _Bool
, дробная часть отбрасывается (т.е. значение усекается до нуля). Если значение интегральной части не может быть представлено целочисленным типом, поведение не определено.
Вы должны проверить диапазон вручную, но не используйте код, подобный :
// DON'T use code like this!
if (my_double > INT_MAX || my_double < INT_MIN)
printf("Overflow!");
INT_MAX
- это целочисленная константа, которая может не иметь точного представления с плавающей точкой . При сравнении с плавающей точкой оно может быть округлено до ближайшего более высокого или ближайшего нижнего представимого значения с плавающей запятой (это определяется реализацией). Например, для 64-разрядных целых чисел INT_MAX
равен 2^63 - 1
, который обычно округляется до 2^63
, поэтому проверка по существу становится my_double > INT_MAX + 1
. Это не обнаружит переполнение, если my_double
равно 2^63
.
Например, с gcc 4.9.1 в Linux, следующая программа
#include <math.h>
#include <stdint.h>
#include <stdio.h>
int main() {
double d = pow(2, 63);
int64_t i = INT64_MAX;
printf("%f > %lld is %s\n", d, i, d > i ? "true" : "false");
return 0;
}
печать
9223372036854775808.000000 > 9223372036854775807 is false
Трудно сделать это правильно, если вы заранее не знаете пределов и внутреннего представления целочисленных и двойных типов. Но если вы, например, преобразуете из double
в int64_t
, вы можете использовать константы с плавающей запятой, которые являются точными двойными числами (при условии дополнения до двух и двойного IEEE):
if (!(my_double >= -9223372036854775808.0 // -2^63
&& my_double < 9223372036854775808.0) // 2^63
) {
// Handle overflow.
}
Конструкция !(A && B)
также правильно обрабатывает NaN. Портативная, безопасная, но немного неточная версия для int
s:
if (!(my_double > INT_MIN && my_double < INT_MAX)) {
// Handle overflow.
}
Это приводит к ошибкам со стороны предостережения и ложному отклонению значений, равных INT_MIN
или INT_MAX
. Но для большинства приложений это должно быть хорошо.