2009-02-08 3 views
21

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

Это поведение определено для всех платформ?
Каков наилучший способ обнаружить это под/переполнение? Помещает ли инструкции if для min и max int перед отличным решением?

+0

[Приведение минимального 32-битного целого числа (-2147483648) в float дает положительное число (2147483648.0)] (http://stackoverflow.com/q/11536389/995714) –

ответ

0

Я не уверен в этом, но я думаю, что возможно «включить» исключения с плавающей запятой для under/overflow ... посмотрите на это Dealing with Floating-point Exceptions in MSVC7\8, чтобы у вас была альтернатива проверкам if/else.

-1

Я не могу точно сказать, определено ли это для всех платформ, но это в значительной степени то, что произошло на каждой платформе, которую я использовал. Кроме того, по моему опыту, он катится. То есть, если значение double равно INT_MAX + 2, тогда, когда результат литья заканчивается INT_MIN + 2.

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

12

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

if (my_double > nextafter(INT_MAX, 0) || my_double < nextafter(INT_MIN, 0)) 
    printf("Overflow!"); 
else 
    my_int = (int)my_double; 

EDIT: nextafter() решит проблему упоминаемый nwellnhof

+0

Это не работает для меня для поплавка. Я пытаюсь сделать это 'float f = INT_MAX; е ++; ConvertToInt (f) 'с лимитом, который у вас выше, и он не переполняется. Что поделаешь? – Pittfall

+1

@Pittfall: 'float' имеет (все, кроме очень экзотических платформ, используют поплавки IEEE-754) имеет 24 значащих двоичных разряда. Поэтому, когда вы установите его в INT_MAX, который равен 2³¹-1 (INT_MAX на 32-битной платформе), последняя цифра составляет 128 секунд. Поэтому, если вы добавите что-нибудь меньшее, чем 128, результатом будет номер orignal, то есть '(float) INT_MAX + 1.f == (float) INT_MAX'. С 'double', который имеет более значимые цифры, чем' int', он будет работать. –

+0

'INT_MAX' и' INT_MIN' - это способ проверки. Метод C++ использует ['std :: numeric_limits :: max()'] (http://en.cppreference.com/w/cpp/types/numeric_limits/max) и '... :: min()'. –

4

A переносимый способ для C++ является использование класса SafeInt:

http://www.codeplex.com/SafeInt

Реализация позволит нормальное сложение/вычитание/etc на тип числа C++, включая отбрасывание. Он будет генерировать исключение всякий раз, когда и сценарий переполнения обнаружен.

SafeInt<int> s1 = INT_MAX; 
SafeInt<int> s2 = 42; 
SafeInt<int> s3 = s1 + s2; // throws 

Я настоятельно рекомендую использовать этот класс в любом месте, где переполнение является важным сценарием. Это делает очень трудным избежать бесшумного переполнения. В случаях, когда есть сценарий восстановления для переполнения, просто поймите SafeIntException и восстановите по мере необходимости.

SafeInt теперь работает на GCC, а также Visual Studio

+1

довольно прохладный. есть ли много накладных расходов? – wprl

+0

Грубый тест заставил меня поверить, что, возможно, невозможно обнаружить переполнение в C++ (без серьезных накладных расходов или полного изменения парадигмы, например, обертывание каждого целого в качестве объекта). Этот _dedicated class_ не может обрабатывать 'SafeInt x = std :: numeric_limits :: max() + 100' (он не выбрасывает). – kizzx2

+2

ты шутишь ...? ваша правая сторона * сделала * уже переполняется, прежде чем она даже дойдет до SafeInt ctor. Вы не можете обвинять SafeInt в том, что – Ichthyo

2

Другой вариант заключается в использовании boost::numeric_cast, который позволяет для любого преобразования между числовыми типами. Он обнаруживает потерю диапазона при преобразовании числового типа и генерирует исключение, если диапазон не может быть сохранен.

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

Конечно, это не обычный C больше ;-)

+0

boost :: numeric_cast делает ту же ошибку, что и в моем ответе. 'numeric_cast (pow (2.0, 63.0))' не выбрасывает. – nwellnhof

11

Чтобы ответить на ваш вопрос: поведение, когда вы приводите из диапазона поплавки не определен или конкретной реализации.

Говоря по опыту: Я работал над системой MIPS64, которая вообще не выполняла эти приемы. Вместо того, чтобы делать что-то детерминированное, процессор бросает исключение CPU.Обработчик исключений, который должен эмулировать бросок, возвращался без каких-либо результатов.

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

Лучше сделайте проверку диапазона, если вы не уверены, что номер не может выйти из допустимого диапазона.

2

Мы встречаем тот же вопрос. таких как:

double d = 9223372036854775807L; 
int i = (int)d; 

в Linux/window, i = -2147483648. но в AIX 5.3 i = 2147483647.

Если двойное значение находится за пределами интерьеров.

  • Linux/window всегда возвращает INT_MIN.
  • AIX вернет INT_MAX, если double является постинжным, вернет INT_MIN double is negetive.
11

При бросании поплавков в целые числа переполнение вызывает неопределенное поведение. Из спецификации 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) также обрабатывает правильно пренебрежимо малых.Портативная, безопасная, но slighty неточной версии для int с является:

if (!(my_double > INT_MIN && my_double < INT_MAX)) { 
    // Handle overflow. 
} 

Это заблуждается на стороне предостережения и ложно отклонять значения, которые равны или INT_MININT_MAX. Но для большинства приложений это должно быть хорошо.

+0

Я только что сделал небольшое эмпирическое тестирование, и этот ответ кажется правильным (опять же, предполагая два целых числа дополнения, если вы не можете предположить, что, возможно, Boost или SafeInt - единственный разумный путь). Вы должны перенести этот ответ и понизить неверный ответ, защищающий my_double> INT_MAX || my_double bhaller

+0

@bhaller Я только что проверил, и оба Boost и SafeInt совершают ту же ошибку, что и в моем ответе. – nwellnhof

+0

Yikes. Вы сообщили им о проблеме? – bhaller

3

Каков наилучший способ обнаружить это под/переполнение?

Сравните усеченный double в точных границ вблизи INT_MIN,INT_MAX.

трюк является точно пределы преобразовывают на основе INT_MIN,INT_MAX в double значений. A double может точно не представлять INT_MAX, поскольку количество бит в intможет превышать точность с плавающей запятой. В этом случае преобразование INT_MAX в double страдает округлением. Число после INT_MAX является степенью-2 и, безусловно, представляется в виде double. 2.0*(INT_MAX/2 + 1) генерирует целое число больше, чем INT_MAX.

То же самое применимо к INT_MIN на машинах, отличных от 2-х.

INT_MAX всегда питания из-2 - 1.
INT_MIN всегда:
-INT_MAX (не 2 дополнение) или
-INT_MAX-1 (2 дополнение)

int double_to_int(double x) { 
    x = trunc(x); 
    if (x >= 2.0*(INT_MAX/2 + 1)) Handle_Overflow(); 
    #if -INT_MAX == INT_MIN 
    if (x <= 2.0*(INT_MIN/2 - 1)) Handle_Underflow(); 
    #else 
    if (x < INT_MIN) Handle_Underflow(); 
    #endif 
    return (int) x; 
} 

Для обнаружения NaN и не используется trunc()

#define DBL_INT_MAXP1 (2.0*(INT_MAX/2+1)) 
#define DBL_INT_MINM1 (2.0*(INT_MIN/2-1)) 

int double_to_int(double x) { 
    if (x < DBL_INT_MAXP1) { 
    #if -INT_MAX == INT_MIN 
    if (x > DBL_INT_MINM1) { 
     return (int) x; 
    } 
    #else 
    if (ceil(x) >= INT_MIN) { 
     return (int) x; 
    } 
    #endif 
    Handle_Underflow(); 
    } else if (x > 0) { 
    Handle_Overflow(); 
    } else { 
    Handle_NaN(); 
    } 
} 
+0

Вы говорите, что мощность 2, размер 'MAX_INT + 1', безусловно, представляется как« double ». Можете ли вы объяснить, почему? Каковы ваши предположения? Достаточно ли IEEE? –

+1

Хорошо, теперь я вижу, что вы считаете, что 'FLT_RADIX' равно 2, а' INT_MAX' находится значительно ниже 'DBL_MAX', и ничего больше, кроме того, что гарантируется языковым стандартом. Это замечательное и творческое решение. Я люблю это. –

+0

Один комментарий: то, что вы называете * underflow *, я считаю, что формально называется * отрицательным переполнением *. Недопустимый поток - это когда результат привязывается к нулю. Переполнение происходит, когда грубо говоря, влияет величина. –

Смежные вопросы