Стандарт IEEE 754 представляет NaN
в виде битовой комбинации с показателем степени 1 и ненулевой дробью (обратите внимание, что все десятичные значения с плавающей запятой представлены знаком, показателем степени и дробь "), то есть много разных NaN, которые вы можете представить, поскольку" ненулевая дробь "может быть множеством разных значений.
Чтобы выделить ваши NaN
представления, расширьте свой код с помощью это:
#include <cmath>
#include <limits>
#include <bitset>
#include <iostream>
union udouble {
double d;
unsigned long long u;
};
void Display(double doubleValue, char* what)
{
udouble ud;
ud.d = doubleValue;
std::bitset<sizeof(double) * 8> b(ud.u);
std::cout << "BitSet : " << b.to_string() << " for " << what << std::endl;
}
int main()
{
using num_lim = std::numeric_limits<double>;
const double NAN_macro = static_cast<double>(NAN); // -nan(ind)
const double NAN_quiet = num_lim::quiet_NaN(); // nan
const double NAN_sig = num_lim::signaling_NaN(); // nan
const double NAN_str = std::nan(""); // nan
const double NAN_str1 = std::nan("1"); // nan
const double NAN_strLong = std::nan("some string"); // nan
...
Display( NAN_macro, "NAN_macro" );
Display( NAN_quiet, "NAN_quiet" );
Display( NAN_sig, "NAN_sig" );
Display( NAN_str, "NAN_str" );
Display( NAN_str1, "NAN_str1" );
Display( NAN_strLong, "NAN_strLong" );
}
Даже если у него есть некоторый UB (это можно исправить, см. комментарий Руслана), это работает, чтобы проиллюстрировать то, что я объясняю ниже), эта программа выводит:
BitSet : 0111111111111000000000000000000000000000000000000000000000000000 for NAN_macro
BitSet : 0111111111111000000000000000000000000000000000000000000000000000 for NAN_quiet
BitSet : 0111111111110100000000000000000000000000000000000000000000000000 for NAN_sig
BitSet : 0111111111111000000000000000000000000000000000000000000000000000 for NAN_str
BitSet : 0111111111111000000000000000000000000000000000000000000000000001 for NAN_str1
BitSet : 0111111111111000000000000000000000000000000000000000000000000000 for NAN_strLong
Все они имеют:
- «0» в качестве знака (вы, вероятно, получите «1» для NAN_macro, так как вы сообщите об этом как -nan, я не знаю). Я использовал g ++, я уверен, что некоторые компиляторы могут определять макрос
NAN
по-разному, я сомневаюсь, что в любом случае это часть стандарта C ++. - "11111111111" как показатель степени
- , тогда разные значения " дробь "... вообще-то, но никогда не только нули, иначе это больше не будет NaN.
На самом деле, это значение" дробь "или" полезная нагрузка "(см. комментарий Руслана) может использоваться чтобы хранить любую информацию, которую вы хотели бы сохранить (например, «почему здесь был нан»?), это называется NaN-бокс .
Именно поэтому в какой-то момент у вас могут быть "разные" значения NaN ( quiet_NaN , signaling_NaN или любые NaN
, которые вы создаете, соответствуют стандарту IEEE 754 .. . даже если у вас разные представления памяти, все это значения NaN
(x!=x
и std::isnan(x)==true
).
Итак, чтобы ответить на ваши вопросы:
почему NAN -nan(ind)
, тогда как std::numeric_limits<double>::quiet_NaN()
- это nan
Скорее всего, потому, что ваш компилятор определил макрос NAN подобным образом, он может отличаться при использовании другого компилятора. Кстати, это как мин / макс макросы, даже если один у них была плохая идея определить их, не используйте их, предпочитайте std
функции, которые являются частью стандарта, поэтому должны работать одинаково с любым компилятором, который вы используете.
почему у нас std::nan("")
и std::nan("1")
, если в конце дня они оба будут казаться одинаковыми.
Может быть, "помочь вам сыграть с NaN-боксом" может быть ответом, даже если я сомневаюсь эти функции были созданы для этой конкретной цели c. Правильный ответ можно просто «позволить вам решить, какую« дробную »величину вы хотите использовать для NaN
, если вам нужно что-то отличное от std::quiet_NaN
и std::signaling_NaN
"
Источники: https://steve.hollasch.net/cgindex/coding/ieeefloat.html
Также используется { ссылка } для вывода NaN
представления памяти.