Как преобразовать сигнализацию NaN в тихий NaN? - PullRequest
2 голосов
/ 18 ноября 2011

Я хочу преобразовать сигнальный NaN в тихий NaN на C. Может кто-нибудь предложить метод?

Спасибо.

Ответы [ 2 ]

4 голосов
/ 18 ноября 2011

Полагаю, я расширю свой комментарий и предоставлю решение.

Сложная часть здесь - возможность прочитать / сравнить 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 и целочисленными единицами.

Вот как это работает:

  1. Объединение, очевидно, используется для получениябиты float в int.
  2. Все NaNs будут иметь биты в наборе 0x7f80000.Тест оператора if проверит, установлены ли все эти биты.
  3. 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?

1 голос
/ 18 ноября 2011

В комментариях вы упоминаете, что вы действительно хотите сделать:

, прежде чем я выполню какую-либо другую операцию, я хотел бы проверить на NaN, то есть число! = Число, но это не будет работатьдля сигнализации NaN

Это то, для чего предназначен макрос C99 isnan(), нет необходимости в number != number.Сам стандарт C на самом деле не определяет семантику сигнализации NaN - все, что он говорит:

a , сигнализирующий NaN , обычно вызывает исключение с плавающей запятой, когда возникает как арифметический операнд

в разделе 5.2.4.2.2 §3 и

Эта спецификация не определяет поведение сигнальных NaN.

в приложенииF, раздел F.2.1 §1.

Однако согласно Википедии , IEEE 754 определяет предикат isNaN () , который не вызывает исключение, даже когда используетсяс сигнализацией NaN, и isnan() должен вести себя соответственно в реализациях, которые обеспечивают семантику с плавающей запятой IEEE.

...