0

Я скомпоновал тестовый костюм с плавающей запятой паранойи в системе pc386 с использованием уровня оптимизации GCC O2 и получил несколько сбоев, но затем скомпилировал его без оптимизации с тем же GCC и получил правильный результат. Я читал о флажках, которые включены в O2, но ни один из них не кажется проблематичным. Что может быть причиной? Код паранойя можно найти here и это взятая выход с оптимизацией O2:Какой флаг gcc O2 может привести к сбою в вычислении fp?

*** 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... 
+3

Я думаю, что это поставило бы за лучший вопрос, если бы вы предоставили детали сбоев, а также соответствующий код (насколько это практически). Знать номер версии 'gcc' тоже не повредит. – NPE

+2

Конечно, если бы вы могли уменьшить один или несколько сбоев в SSCCE (http://sscce.org/), это было бы еще лучше. – NPE

+0

Недавняя версия GCC на современном оборудовании может воспользоваться опциями '-msse2 -mfpmath = sse' для генерации сборки, которая вычисляет каждое выражение точно до точности его типа. Также может быть информативным посмотреть исходный код Paranoia с точки зрения этого описания: http://gcc.gnu.org/ml/gcc-patches/2008-11/msg00105.html. Если недавний GCC уже не создает строгий код IEEE 754 для плавающей запятой, либо '-std = c99', либо' -fexcess-precision = standard', то сгенерированная сборка будет соответствовать интерпретации, изложенной Джозефом С. Майерсом. –

ответ

3

Оптимизация и -O2 не является основным виновником здесь. Набор тестов, который вы запускаете, может завершиться неудачей в реализации C с другими сценариями оптимизации. Основная проблема в этом случае заключается в том, что тест Paranoia проверяет, является ли арифметика с плавающей точкой согласованной и имеет различные свойства, но арифметика с плавающей запятой в реализации 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 допускает такое поведение.

Я не рассматривал сбои, сообщенные после первого. Они, вероятно, связаны с аналогичными причинами.

+0

Версия GCC - 4.4.7. Оптимизирует ли оптимизация с плавающей запятой? Если это не так, почему компиляция без оптимизации приводит к правильным ответам? –

+0

@ ArdalanPouyabahar Старые версии GCC не гарантируют, что те же результаты будут получены вычислениями с плавающей запятой с оптимизацией и без нее. Простые вычисления могут выполняться во время компиляции с другой семантикой, чем семантика времени выполнения. См. Http://arxiv.org/abs/cs/0701192 для довольно полного отчета о трудностях предсказания поведения с плавающей запятой с этим видом компилятора или http://blog.frama-c.com/index.php ? post/2013/07/06/On-the-exact-analysis-of-C-programs-for-FLT_EVAL_METHOD-2 и http://blog.frama-c.com/index.php?post/2013/07/24/More-on-FLT_EVAL_METHOD_2 –

+0

@PascaCuoq Ссылки были настолько полезными, но когда я проверил версию 4.4.7 на 2012 год, есть ли эта проблема? –