Полагаю, я расширю свой комментарий и предоставлю решение.
Сложная часть здесь - возможность прочитать / сравнить sNaN
без вызова исключения.В конце концов, это называется «сигнализация» по причине. Википедия говорит, что даже операции сравнения на sNaN
вызовут исключение.
Так что прямое использование number != number
или isnan(value)
, вероятно, не сработает, потому что они вызывают сравнения и будутвызвать аппаратное исключение.(Я не совсем уверен, как реализован isnan(value)
.)
РЕДАКТИРОВАТЬ: Исправление, похоже, isnan()
никогда не вызовет исключение даже при передаче сигнала NaN
так что остальная часть этого ответа становится бессмысленной.
Предикат isNaN (x) определяет, является ли значение NaN, и никогда не сигнализирует об исключении, даже если x является сигнальным NaN.
То есть это можно сделать так, как это было предложено Кристофом в комментариях:
if(isnan(value))
value = NAN;
Вот мой оригинальный ответ, который не использует isnan(value)
:
Таким образом, я могу думать только о том, чтобы сделать это побитовым путем.
Предполагая, что float
является стандартной IEEE с одинарной точностью, а int
- 32-битнойцелое, тогда вот один из способов: (Обратите внимание, что я не проверял это.)
union{
int i;
float f;
} val;
val.f = // Read the value here.
// If NaN, force it to a quiet NaN.
if ((val.i & 0x7f800000) == 0x7f800000){
val.i |= 0x00400000;
}
Обратите внимание, что этот подход не полностью совместим с C и будет вызывать поведение, определяемое реализацией.Также обратите внимание, что этот подход не особенно эффективен из-за необходимости перемещать данные между FP и целочисленными единицами.
Вот как это работает:
- Объединение, очевидно, используется для получениябиты
float
в int
. - Все
NaNs
будут иметь биты в наборе 0x7f80000
.Тест оператора if проверит, установлены ли все эти биты. i |= 0x00400000;
заставляет NaN
в тихое состояние NaN
.Бит 22 определяет, является ли NaN
тихим или тихим.Если установить значение 1, оно станет тихим NaN
.
РЕДАКТИРОВАТЬ 2: Если вы не можете использовать союзы, вот несколько других подходов (каждый изкоторые имеют свои недостатки):
Метод 1:
float f = // Read the value here.
int i = *(int*)&f;
if ((i & 0x7f800000) == 0x7f800000){
i |= 0x00400000;
}
f = *(float*)&i;
Недостаток: Это нарушает строгий псевдоним, но, вероятно, все еще будет работать.
Метод 2:
char buf[sizeof(float)];
float f = // Read the value here.
*(float*)&buf = f;
int i = *(int*)&buf;
if ((i & 0x7f800000) == 0x7f800000){
i |= 0x00400000;
}
*(int*)&buf = i;
f = *(float*)&buf;
Та же идея работает с memcpy()
.
Недостаток: Если выравнивание имеет значение,Вы должны убедиться, что buf
выровнен.
Метод 3: Реализация своего собственного isnan()
:
См. этот вопрос: Где источниккод для isnan?