Я був членом комітету IEEE-754, я спробую трохи допомогти з’ясувати речі.
По-перше, числа з плаваючою комою не є дійсними числами, а арифметика з плаваючою комою не відповідає аксіомам реальної арифметики. Трихотомія - не єдина властивість справжньої арифметики, яка не тримає плавців, ані навіть найважливішу. Наприклад:
- Доповнення не асоціативне.
- Закон про розподіл не дотримується.
- Існують числа з плаваючою комою без зворотів.
Я міг би продовжувати. Неможливо вказати арифметичний тип фіксованого розміру, який задовольняє всі властивості реальної арифметики, які ми знаємо і любимо. Комітет 754 повинен вирішити згорнути або зламати деякі з них. Цим керуються деякі досить прості принципи:
- Коли ми можемо, ми відповідаємо поведінці реальної арифметики.
- Коли ми не можемо, ми намагаємось зробити порушення максимально передбачуваними та простішими для діагностики.
Щодо Вашого коментаря "це не означає, що правильна відповідь неправдива", це неправильно. Присудок (y < x)
запитує, чи y
менший за x
. Якщо y
це NaN, то воно не менше будь-якого значення з плаваючою комою x
, тому відповідь обов'язково хибна.
Я згадував, що трихотомія не має значення для значень з плаваючою комою. Однак є схожа властивість, яка дійсно дотримується. Пункт 2 статті 5.11 стандарту 754-2008:
Можливі чотири взаємовиключні відносини: менші, рівні, більші та не упорядковані. Останній випадок виникає, коли щонайменше один операнд є NaN. Кожен NaN повинен порівнювати не упорядкований з усім, включаючи себе.
Що стосується написання додаткового коду для обробки NaN, то зазвичай можна (хоча і не завжди просто) структурувати свій код таким чином, щоб NaNs потрапляли належним чином, але це не завжди так. Коли це не так, може знадобитися якийсь додатковий код, але це невелика ціна, щоб заплатити за зручність, яку алгебраїчне закриття принесло арифметиці з плаваючою комою.
Додаток: Багато коментаторів стверджують, що корисніше зберегти рефлексивність рівності та трихотомії на тій підставі, що прийняття NaN! = NaN не схоже на жодну звичну аксіому. Я зізнаюся, що маю певну симпатію до цієї точки зору, тому думав, що перегляну цю відповідь і надам трохи більше контексту.
Я розумію, як говорити з Каханом, що NaN! = NaN виник із двох прагматичних міркувань:
Це x == y
повинно бути еквівалентно, x - y == 0
коли це можливо (крім того, що є теоремою реальної арифметики, це робить апаратну реалізацію порівняння більш просторовою, що було надзвичайно важливим на момент розробки стандарту - зауважте, що це порушено для x = y = нескінченність, тому це не є великою причиною сама по собі; її можна було б розумно схилити (x - y == 0) or (x and y are both NaN)
).
Що ще важливіше, isnan( )
в той час, коли NaN був формалізований в арифметиці 8087 року , не було присудка; необхідно було забезпечити програмістів зручним та ефективним засобом виявлення значень NaN, які не залежали від мов програмування, забезпечуючи щось подібне, isnan( )
що може зайняти багато років. Я цитую власне письмо Кахана на цю тему:
Якби не було можливості позбутися від NaN, вони були б такими марними, як Індефініти на КРЕЙ; як тільки хтось зіткнувся, обчислення було б краще зупинити, а не продовжувати на невизначений час до невизначеного висновку. Ось чому деякі операції над NaN повинні забезпечити результати, що не належать до NaN. Які операції? ... Виняток становлять предикати C "x == x" і "x! = X", які відповідно дорівнюють 1 і 0 для кожного нескінченного або кінцевого числа x, але зворотне, якщо x не число (NaN); вони забезпечують єдине просте необмежене розрізнення NaN та чисел у мовах, у яких відсутні слова для NaN та присудка IsNaN (x).
Зауважте, що це також логіка, яка виключає повернення чогось на кшталт "не-булевого". Можливо, цей прагматизм був неправильним, і стандарт повинен був би вимагати isnan( )
, але це зробило б NaN майже неможливим ефективно та зручно використовувати протягом декількох років, поки світ чекав на прийняття мови програмування. Я не впевнений, що це було б розумним компромісом.
Якщо бути тупим: результат NaN == NaN зараз не зміниться. Краще навчитися жити з цим, ніж скаржитися в Інтернеті. Якщо ви хочете стверджувати, що також має існувати співвідношення порядку, яке підходить для контейнерів , я б рекомендував рекомендувати, щоб ваша улюблена мова програмування реалізувала totalOrder
предикат, стандартизований в IEEE-754 (2008). Той факт, що він ще не говорить про справедливість турботи Кахана, яка мотивувала сучасний стан справ.
while (fabs(x - oldX) > threshold)
, виходить із циклу, якщо відбувається конвергенція або в обчислення входить NaN. Виявлення NaN та відповідного засобу може відбуватися поза циклом.