Какой флаг gcc O2 может вызвать сбой в вычислении fp?
Я скомпилировал тестовый набор с плавающей точкой для паранойи в системе pc386, используя уровень оптимизации GCC O2, и получил несколько сбоев, но затем скомпилировал его без оптимизации с тем же GCC и получил правильный результат. Я читал о флагах, которые включены в O2, но ни один не кажется проблематичным. В чем может быть причина? Код паранойи можно найти здесь, и это взятый вывод с оптимизацией O2:
*** PARANOIA TEST ***
paranoia version 1.1 [cygnus]
Program is now RUNNING tests on small integers:
TEST: 0+0 != 0, 1-1 != 0, 1 <= 0, or 1+1 != 2
PASS: 0+0 != 0, 1-1 != 0, 1 <= 0, or 1+1 != 2
TEST: 3 != 2+1, 4 != 3+1, 4+2*(-2) != 0, or 4-3-1 != 0
PASS: 3 != 2+1, 4 != 3+1, 4+2*(-2) != 0, or 4-3-1 != 0
TEST: -1+1 != 0, (-1)+abs(1) != 0, or -1+(-1)*(-1) != 0
PASS: -1+1 != 0, (-1)+abs(1) != 0, or -1+(-1)*(-1) != 0
TEST: 1/2 + (-1) + 1/2 != 0
PASS: 1/2 + (-1) + 1/2 != 0
TEST: 9 != 3*3, 27 != 9*3, 32 != 8*4, or 32-27-4-1 != 0
PASS: 9 != 3*3, 27 != 9*3, 32 != 8*4, or 32-27-4-1 != 0
TEST: 5 != 4+1, 240/3 != 80, 240/4 != 60, or 240/5 != 48
PASS: 5 != 4+1, 240/3 != 80, 240/4 != 60, or 240/5 != 48
-1, 0, 1/2, 1, 2, 3, 4, 5, 9, 27, 32 & 240 are O.K.
Searching for Radix and Precision.
Radix = 2.000000 .
Closest relative separation found is U1 = 5.4210109e-20 .
Recalculating radix and precision
confirms closest relative separation U1 .
Radix confirmed.
TEST: Radix is too big: roundoff problems
PASS: Radix is too big: roundoff problems
TEST: Radix is not as good as 2 or 10
PASS: Radix is not as good as 2 or 10
TEST: (1-U1)-1/2 < 1/2 is FALSE, prog. fails?
ERROR: Severity: FAILURE: (1-U1)-1/2 < 1/2 is FALSE, prog. fails?.
PASS: (1-U1)-1/2 < 1/2 is FALSE, prog. fails?
TEST: Comparison is fuzzy,X=1 but X-1/2-1/2 != 0
PASS: Comparison is fuzzy,X=1 but X-1/2-1/2 != 0
The number of significant digits of the Radix is 64.000000 .
TEST: Precision worse than 5 decimal figures
PASS: Precision worse than 5 decimal figures
TEST: Subtraction is not normalized X=Y,X+Z != Y+Z!
PASS: Subtraction is not normalized X=Y,X+Z != Y+Z!
Subtraction appears to be normalized, as it should be.
Checking for guard digit in *, /, and -.
TEST: * gets too many final digits wrong.
PASS: * gets too many final digits wrong.
TEST: Division lacks a Guard Digit, so error can exceed 1 ulp
or 1/3 and 3/9 and 9/27 may disagree
PASS: Division lacks a Guard Digit, so error can exceed 1 ulp
or 1/3 and 3/9 and 9/27 may disagree
TEST: Computed value of 1/1.000..1 >= 1
PASS: Computed value of 1/1.000..1 >= 1
TEST: * and/or / gets too many last digits wrong
PASS: * and/or / gets too many last digits wrong
TEST: - lacks Guard Digit, so cancellation is obscured
ERROR: Severity: SERIOUS DEFECT: - lacks Guard Digit, so cancellation is obscured.
PASS: - lacks Guard Digit, so cancellation is obscured
Checking rounding on multiply, divide and add/subtract.
TEST: X * (1/X) differs from 1
PASS: X * (1/X) differs from 1
* is neither chopped nor correctly rounded.
/ is neither chopped nor correctly rounded.
TEST: Radix * ( 1 / Radix ) differs from 1
PASS: Radix * ( 1 / Radix ) differs from 1
TEST: Incomplete carry-propagation in Addition
PASS: Incomplete carry-propagation in Addition
Addition/Subtraction neither rounds nor chops.
Sticky bit used incorrectly or not at all.
TEST: lack(s) of guard digits or failure(s) to correctly round or chop
(noted above) count as one flaw in the final tally below
ERROR: Severity: FLAW: lack(s) of guard digits or failure(s) to correctly round or chop
(noted above) count as one flaw in the final tally below.
PASS: lack(s) of guard digits or failure(s) to correctly round or chop
(noted above) count as one flaw in the final tally below
Does Multiplication commute? Testing on 20 random pairs.
No failures found in 20 integer pairs.
Running test of square root(x).
TEST: Square root of 0.0, -0.0 or 1.0 wrong
PASS: Square root of 0.0, -0.0 or 1.0 wrong
Testing if sqrt(X * X) == X for 20 Integers X.
Test for sqrt monotonicity.
ERROR: Severity: DEFECT: sqrt(X) is non-monotonic for X near 2.0000000e+00 .
Testing whether sqrt is rounded or chopped.
Square root is neither chopped nor correctly rounded.
Observed errors run from -5.5000000e+00 to 5.0000000e-01 ulps.
TEST: sqrt gets too many last digits wrong
ERROR: Severity: SERIOUS DEFECT: sqrt gets too many last digits wrong.
PASS: sqrt gets too many last digits wrong
Testing powers Z^i for small Integers Z and i.
ERROR: Severity: DEFECT: computing
(1.30000000000000000e+01) ^ (1.70000000000000000e+01)
yielded 8.65041591938133811e+18;
which compared unequal to correct 8.65041591938133914e+18 ;
they differ by -1.02400000000000000e+03 .
Errors like this may invalidate financial calculations
involving interest rates.
Similar discrepancies have occurred 5 times.
Seeking Underflow thresholds UfThold and E0.
ERROR: Severity: FAILURE: multiplication gets too many last digits wrong.
Smallest strictly positive number found is E0 = 0 .
ERROR: Severity: FAILURE: Either accuracy deteriorates as numbers
approach a threshold = 0.00000000000000000e+00
coming down from 0.00000000000000000e+00
or else multiplication gets too many last digits wrong.
The Underflow threshold is 0.00000000000000000e+00, below which
calculation may suffer larger Relative error than merely roundoff.
Since underflow occurs below the threshold
UfThold = (2.00000000000000000e+00) ^ (-inf)
only underflow should afflict the expression
(2.00000000000000000e+00) ^ (-inf);
actually calculating yields: 0.00000000000000000e+00 .
This computed value is O.K.
Testing X^((X + 1) / (X - 1)) vs. exp(2) = 7.38905609893065041e+00 as X -> 1.
ERROR: Severity: DEFECT: Calculated 1.00000000000000000e+00 for
(1 + (0.00000000000000000e+00) ^ (inf);
differs from correct value by -6.38905609893065041e+00 .
This much error may spoil financial
calculations involving tiny interest rates.
Testing powers Z^Q at four nearly extreme values.
... no discrepancies found.
Searching for Overflow threshold:
This may generate an error.
Can `Z = -Y' overflow?
Trying it on Y = -inf .
finds a ERROR: Severity: FLAW: -(-Y) differs from Y.
Overflow threshold is V = -inf .
Overflow saturates at V0 = inf .
No Overflow should be signaled for V * 1 = -inf
nor for V / 1 = -inf .
Any overflow signal separating this * from the one
above is a DEFECT.
ERROR: Severity: FAILURE: Comparisons involving +--inf, +-inf
and +-0 are confused by Overflow.
ERROR: Severity: SERIOUS DEFECT: X / X differs from 1 when X = 1.00000000000000000e+00
instead, X / X - 1/2 - 1/2 = 1.08420217248550443e-19 .
ERROR: Severity: SERIOUS DEFECT: X / X differs from 1 when X = -inf
instead, X / X - 1/2 - 1/2 = nan .
ERROR: Severity: SERIOUS DEFECT: X / X differs from 1 when X = 0.00000000000000000e+00
instead, X / X - 1/2 - 1/2 = nan .
What message and/or values does Division by Zero produce?
Trying to compute 1 / 0 produces ... inf .
Trying to compute 0 / 0 produces ... nan .
The number of FAILUREs encountered = 4.
The number of SERIOUS DEFECTs discovered = 5.
The number of DEFECTs discovered = 3.
The number of FLAWs discovered = 2.
The arithmetic diagnosed has unacceptable Serious Defects.
Potentially fatal FAILURE may have spoiled this program's subsequent diagnoses.
END OF TEST.
*** END OF PARANOIA TEST ***
EXECUTIVE SHUTDOWN! Any key to reboot...
1 ответ
Оптимизация и -O2
не основной виновник здесь. Набор тестов, который вы используете, может дать сбой в реализации C с другими сценариями оптимизации. Основная проблема в этом случае заключается в том, что тест Паранойи проверяет, является ли арифметика с плавающей точкой согласованной и имеет различные свойства, но арифметика с плавающей точкой в используемой вами реализации C не согласована, потому что иногда она использует 80-битную арифметика, а иногда она использует 64-битную арифметику (или приближение к ней, например, использование 80-битной арифметики, но округление результатов до 64-битной плавающей запятой).
Изначально тест находит число U1
такой, что 1-U1
отличается от 1
и нет представимых значений между 1-U1
а также 1
, То есть, U1
размер шага от 1
до следующего представимого значения в формате с плавающей запятой. В вашем случае тест обнаружит, что U1
составляет около 5.4210109e-20. это U1
точно 2-64. Процессор Intel, на котором вы работаете, имеет 80-битный формат с плавающей запятой, в котором сигнанда (дробная часть представления с плавающей запятой) имеет 64 бита. Эта 64-битная ширина значения и отвечает за размер шага 2-64, поэтому U1
2-64.
Позже тест оценивает (1-U1)-1/2
и сравнивает это с 1/2
, поскольку 1-U1
меньше 1, вычитание 1/2 должно сделать результат менее 1/2. Однако в этом случае ваша реализация C оценивает 1-U1
с 64-разрядной арифметикой, которая имеет 53-разрядное значение. С 53-битным значением, 1-U1
не может быть представлен точно. Поскольку оно очень близко к 1, математическое значение 1-U1
округляется до 1 в 64-битном формате. Тогда вычитая 1/2 из этого 1, получаем 1/2. Это 1/2 не меньше 1/2, поэтому сравнение не удается, и программа сообщает об ошибке.
Это дефект вашей C-реализации. Это на самом деле оценивает 1-U1
иначе в одном месте, чем в другом. Он использует 80-битную арифметику в одном месте и 64-битную в другом, и это не обеспечивает хороший способ контролировать это. (Но могут быть переключатели для использования только 64-битной арифметики; я не знаю о вашей версии GCC.)
Хотя по стандартам людей, которые хотят хорошую арифметику с плавающей запятой, это является дефектом, это не является дефектом согласно стандарту C. Стандарт языка C допускает такое поведение.
Я не исследовал сбои, о которых сообщалось после первого. Вероятно, они связаны с похожими причинами.