Переполнение с плавающей точкой и неточность - PullRequest
1 голос
/ 11 марта 2020

Я обнаружил некоторые несоответствия в обработке ошибок с плавающей запятой на оборудовании Intel, и мне интересно, является ли это аппаратной ошибкой Intel или просто общим способом работы арифметики с плавающей запятой c. Скэнар ios:

1) 10000 + maxfloat = 3.40282e + 38 ошибка произведена: FE_INEXACT

2) maxfloat + maxfloat = inf ошибки: FE_OVERFLOW, FE_INEXACT

3 ) 1.1 * maxfloat = inf error: FE_OVERFLOW, FE_INEXACT

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

I не могу понять, почему я не получаю переполнение и число просто насыщается в первом случае, но во втором и третьем число не насыщается, и я получаю переполнение.

#include <iostream>
#include <limits>
#include <cstdio>
#include <cfenv>

void print_error() {    
    const int err = fetestexcept(FE_ALL_EXCEPT);
    if (err & FE_INVALID) cout << "FE_INVALID " << endl;            
    if (err & FE_DIVBYZERO) cout << "FE_DIVBYZERO "<< endl;
    if (err & FE_OVERFLOW) cout << "FE_OVERFLOW "<< endl;        
    if (err & FE_UNDERFLOW) cout << "FE_UNDERFLOW " << endl;
    if (err & FE_INEXACT) cout << "FE_INEXACT " << endl;
    cout << endl;      
}

int main() {
    feclearexcept(FE_ALL_EXCEPT);        
    cout << numeric_limits<float>::max() + 100000.0f << endl;
    print_error();

    feclearexcept(FE_ALL_EXCEPT);        
    cout << numeric_limits<float>::max() + numeric_limits<float>::max() << endl;
    print_error();    

    feclearexcept(FE_ALL_EXCEPT);
    cout << 1.1f*numeric_limits<float>::max() << endl;
    print_error();
}

Ответы [ 2 ]

3 голосов
/ 11 марта 2020

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

Сумма 10000 + maxfloat точно не представима следовательно FE_INEXACT. Вместо этого сумма была округлена. Выбор округления включает в себя наибольшее конечное число maxfloat и следующее наибольшее конечное число «как будто», которое может быть представлено с дополнительным диапазоном экспоненты. С округлением до ближайшего, сумма округляется до maxfloat, как это ближе.

В случаях 2 и 3, сумма округляется до или выше этого следующего наибольшего конечного числа «как будто». Поскольку округленная сумма соответствует / превышает это число, возвращается бесконечность.


Ниже приведена числовая строка, показывающая последние 3 конечных числа с плавающей точкой, включая FLT_MAX.
Если бы число с плавающей точкой имело дальнейший диапазон экспонент, следующие 2 числа после FLT_MAX были бы двумя справа: 'FLT_MAX next "как будто" и безымянным.
"На полпути" находится между FLT_MAX и следующим по величине конечным ", как будто "number.

Когда сумма больше FLT_MAX, но меньше" Half-way ", округление до ближайшего результата приводит к FLT_MAX (Случай 1). Когда сумма больше, результат равен бесконечности. (Дело 2,3).

enter image description here

2 голосов
/ 11 марта 2020

Способ округления работает для конечных результатов в верхнем конце конечного диапазона:

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

Логика c здесь такова, что, если при округлении все равно будет получено число в диапазоне экспонент, то нет переполнение (даже если математический результат превысил максимальное представимое конечное число, если нормальное округление вернуло бы его в диапазон).

Максимальное конечное значение в 32-разрядном двоичном коде IEEE-754 равно 2 * 1012 128 * -2 104 . Если бы диапазон экспонент был неограниченным, следующее представимое значение было бы 2 128 .

Давайте сначала рассмотрим случаи 2 и 3. В случае 2 мы добавляем к нему максимальное конечное значение, поэтому имеем (2 128 -2 104 ) + (2 128 -2 104 ). Математически это 2 129 -2 105 . Если бы диапазон показателей был неограниченным, это было бы представимо, поэтому не было бы необходимости в округлении; это будет результат. Тогда это число имеет показатель степени, выходящий за пределы фактического диапазона показателей, поэтому вместо него создается бесконечность.

В случае 3 мы умножаем максимальное конечное значение на 1,1 (которое на самом деле должно быть значением около 1,1, поскольку само значение 1,1 не представимо). Таким образом, мы имеем около (2 128 -2 104 ) • 1.1. Результат действительного числа был бы более 2 128 , поэтому, если бы диапазон экспоненты был неограниченным, результат с плавающей точкой был бы более 2 128 . У этого числа показатель степени находится за пределами фактического диапазона показателей, поэтому вместо него создается бесконечность.

Теперь вернемся к случаю 1. Мы добавляем 10 000 к максимальному конечному значению, поэтому мы имеем 10 000 + (2 128 -2 104 ) или 2 128 -2 104 + 10000. Средняя точка между максимальным конечным значением (2 128 -2 104 ) и следующим значением, которое может быть представлено в неограниченном диапазоне показателей (2 128 ), составляет 2 * * 128 тысяча пятьдесят семь -2 105 . Результат нашего действительного числа, 2 128 -2 104 + 10000, меньше этой средней точки. Таким образом, при использовании округления до ближайших связей до четного мы округляем этот результат до 2 128 -2 104 . Это число находится в пределах фактического диапазона экспоненты (его экспонента равна 127 - мы только что выразили его как 2 128 минус бит вместо 2 127 плюс много). Так что это результат.

Таким образом, 10 000 плюс максимальное конечное значение дают максимальное конечное значение. Он только превысил максимальное конечное значение «немного» и был округлен в меньшую сторону. Другие операции превысили максимальное конечное значение и были округлены.

...