Стандарт IEEE 754-2008 для арифметики з плаваючою комою та стандарт незалежної арифметики (LIA) ISO / IEC 10967, частина 1 відповідають, чому це так.
IEEE 754 § 6.3 Біт знака
Якщо або вхід, або результат - NaN, цей стандарт не інтерпретує ознаку NaN. Однак зауважте, що операції над бітовими рядками - copy, negate, abs, copySign - задають біт знаку результату NaN, інколи заснований на біті знаків операнду NaN. На логічний предикат totalOrder також впливає біт знаків операнду NaN. Для всіх інших операцій цей стандарт не вказує біт знаків результату NaN, навіть коли є лише один вхідний NaN або коли NaN виробляється з недійсної операції.
Коли ані вхідні дані, ані результат не є NaN, знак продукту чи коефіцієнта є виключним АБО знаками операндів; знак суми або різниці x - y, що розглядається як сума x + (−y), відрізняється щонайменше від одного з ознак додавання; а знак результату перетворень, операція квантування, операція roundTo-Integral і roundToIntegralExact (див. 5.3.1) є знаком першого або єдиного операнда. Ці правила застосовуються навіть тоді, коли операнди або результати дорівнюють нулю або нескінченності.
Коли сума двох операндів з протилежними знаками (або різниця двох операндів з подібними знаками) дорівнює рівно нулю, знак цієї суми (або різниці) повинен бути +0 у всіх атрибутах напрямку округлення, крім roundTowardNegative; під цим атрибутом знак точної нульової суми (або різниці) має бути −0. Однак x + x = x - (−x) зберігає той самий знак, що і x, навіть якщо x дорівнює нулю.
Справа доповнення
У режимі округлення за замовчуванням (Round-to-Near, Ties-to Even) ми бачимо, що x+0.0виробляє x, EXCEPT, коли xє -0.0: У цьому випадку ми маємо суму двох операндів з протилежними знаками, сума яких дорівнює нулю, а пункт 6.3 3 правила виробляє це додаток +0.0.
Оскільки +0.0НЕ побітовий ідентичний оригіналу -0.0, і що -0.0є законним значенням , яке може статися в якості вхідних даних, компілятор зобов'язаний помістити в коді , який перетворює потенційні негативні нулі +0.0.
Підсумок: У режимі округлення за замовчуванням, в x+0.0, якщоx
- не є
-0.0 , то xсаме по собі є прийнятним вихідним значенням.
- є
-0.0 , тоді вихідне значення повинно бути таким +0.0 , яке не є бітовим ідентичним -0.0.
Справа множення
У режимі округлення за замовчуванням такої проблеми не виникає x*1.0. Якщо x:
- - це (під) нормальне число,
x*1.0 == xзавжди.
- є
+/- infinity, тоді результат має +/- infinityтой самий знак.
є NaN, то відповідно до
IEEE 754 § 6.2.3 Розмноження NaN
Операція, яка поширює операнд NaN за його результатом і має єдиний NaN як вхід, повинна виробляти NaN з корисним навантаженням вхідного NaN, якщо воно є представленим у форматі призначення.
що означає , що показник і мантиса (хоча і це не знак) NaN*1.0є рекомендується , щоб бути незмінними від входу NaN. Знак не визначений відповідно до пункту 6.3p1 вище, але реалізація може вказати, що він є ідентичним джерелу NaN.
- є
+/- 0.0, тоді результат - це 0зі своїм бітовим знаком XOR з бітом знака 1.0, відповідно до §6.3p2. Оскільки біт знака 1.0є 0, вихідне значення не змінюється від вхідного. Таким чином, x*1.0 == xнавіть коли xдорівнює (негативний) нуль.
Випадок віднімання
У режимі округлення за замовчуванням віднімання x-0.0також є неоперативним, оскільки воно еквівалентно x + (-0.0). Якщо xє
- є
NaN, тоді §6.3p1 і §6.2.3 застосовуються приблизно так само, як і для додавання та множення.
- є
+/- infinity, тоді результат має +/- infinityтой самий знак.
- - це (під) нормальне число,
x-0.0 == xзавжди.
- є
-0.0, то в §6.3p2 ми маємо " [...] знак суми або різниці x - y, що розглядається як сума x + (−y), відрізняється щонайменше від одного з ознак додавання; ". Це змушує нас призначати -0.0як результат (-0.0) + (-0.0), тому що -0.0відрізняється за ознакою від жодного з доповнень, а +0.0відрізняється за ознакою від двох доданків, порушуючи цей пункт.
- є
+0.0, то це зводиться до випадку додавання, (+0.0) + (-0.0)розглянутого вище у «Справі доповнення» , який згідно §6.3p3 постановляється +0.0.
Оскільки для всіх випадків вхідне значення є законним як вихід, допустимо вважати x-0.0не-оп і x == x-0.0тавтологію.
Оптимізація змін цінності
Стандарт IEEE 754-2008 містить таку цікаву цитату:
IEEE 754 § 10.4 Буквальне значення та оптимізація змін цінності
[...]
Наступні перетворення, що змінюють значення, зберігають буквальне значення вихідного коду:
- Застосування властивості ідентичності 0 + x, коли x не дорівнює нулю і не є сигналом NaN, і результат має той же показник, що і x.
- Застосування властивості ідентичності 1 × x, коли x не є сигналом NaN, і результат має той же показник, що і x.
- Зміна корисного вантажу або знака тихого NaN.
- [...]
Оскільки всі NaN та всі нескінченності мають однаковий показник, а правильно округлений результат x+0.0та x*1.0для кінцевих xмає точно таку ж величину, як xі їх показник однаковий.
sNaNs
Сигналізаційні NaN - це пастки з плаваючою комою; Вони є спеціальними значеннями NaN, використання яких в якості операнда з плаваючою комою призводить до недійсного виключення операції (SIGFPE). Якби цикл, який викликає виняток, був оптимізований, програмне забезпечення більше не поводилось би так.
Однак, як в коментарях вказує user2357112 , стандарт C11 явно не визначає поведінку сигналізації NaNs ( sNaN), тому компілятору дозволяється припускати, що вони не виникають, і таким чином, що винятки, які вони викликають, також не трапляються. Стандарт C ++ 11 пропускає опис поведінки для сигналізації NaN, і, таким чином, також залишає її невизначеною.
Режими округлення
У альтернативних режимах округлення допустимі оптимізації можуть змінюватися. Наприклад, у режимі « Круглого до негативного» - нескінченність оптимізація x+0.0 -> xстає допустимою, але x-0.0 -> xстає забороненою.
Щоб GCC не передбачала режими та способи округлення за замовчуванням, експериментальний прапор -frounding-mathможна передати GCC.
Висновок
Clang та GCC , навіть на -O3, залишаються сумісними з IEEE-754. Це означає, що він повинен дотримуватися вищезазначених правил стандарту IEEE-754. x+0.0це не бит ідентичний з xдля всіх в xвідповідно з цими правилами, але x*1.0 можуть бути обрані так : Тобто, коли ми
- Дотримуйтесь рекомендації пройти незмінним корисним навантаженням,
xколи це NaN.
- Залиште біт знака результату NaN без змін
* 1.0.
- Дотримуйтесь розпорядження XOR біт знаку під час показника / продукту, коли
xце не NaN.
Для активізації IEEE-754-небезпечної оптимізації (x+0.0) -> xпрапор -ffast-mathпотрібно передати Clang або GCC.