【问题标题】:How to convert signalling NaN to quiet NaN?如何将信令 NaN 转换为安静的 NaN?
【发布时间】:2012-01-01 02:50:43
【问题描述】:

我想在 C 中将信号 NaN 转换为安静的 NaN。有人可以建议一种方法吗?

谢谢。

【问题讨论】:

  • 你能举个例子吗?我无法理解你的意思?
  • 阅读此question 让我相信您可以通过使用信号 NaN 初始化变量来做到这一点。
  • 这就是你要找的东西:if(isnan(value)) value = NAN;
  • 我认为这里的棘手部分是能够在不触发异常的情况下读取 sNaN。毕竟它被称为“信号”是有原因的。即使与 sNaN 比较也会导致异常 (en.wikipedia.org/wiki/Floating_point#NaNs)。
  • @Mysticial: isnan() 不应引发 fp 异常;我在 C 标准中没有找到任何关于它的内容,但是 afaik 明确提到了 IEEE 754 的 isNaN() 谓词,大多数实现至少尝试遵循......

标签: c floating-point nan


【解决方案1】:

我想我会扩展我的评论并提供解决方案。

这里的棘手部分是能够在不触发异常的情况下读取/比较sNaN。毕竟它被称为“信号”是有原因的。 Wikipedia says that even comparison operations on sNaN will trigger an exception.

所以直接使用number != numberisnan(value) 可能不起作用,因为它们会调用比较并且会触发硬件异常。 (不过我不完全确定isnan(value) 是如何实现的。)

编辑:更正,看起来isnan() 将永远不会触发异常,即使是在信号NaN 上,所以这个答案的其余部分毫无意义。

谓词 isNaN(x) 确定一个值是否为 NaN 并且从不 发出异常信号,即使 x 是信号 NaN。

这意味着它可以按照 Chrisoph 在 cmets 中的建议进行:

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:如果您不能使用联合,这里有一些其他方法(每种方法都有自己的缺点):

方法一:

float f = //  Read the value here.

int i = *(int*)&f;
if ((i & 0x7f800000) == 0x7f800000){
    i |= 0x00400000;
}

f = *(float*)&i;

缺点:它违反了严格的别名,但可能仍然有效。

方法二:

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()

看到这个问题:Where is the source code for isnan?

【讨论】:

  • 谢谢!不幸的是,我不允许使用联合 :( 而且我的环境中没有 isnan() 。
  • 有没有办法在没有联合的情况下屏蔽第 22 位?如果是,那么这就是我正在寻找的。通常的类型转换/屏蔽仍然会导致信号 NaN 崩溃。
  • @kartik: memcpy() 将值添加到int 变量可能 工作,特别是如果 fp 变量是 volatile(但是,它会携带性能惩罚);看来您可能需要降到 asm 级别...
  • 你也可以使用指针转换。要绕过严格混叠,您可以首先通过 char 缓冲区来完成,因为 char* 不受严格混叠规则的约束。如果你想要最大的效率,你可以使用 SSE 内在函数来做所有事情,这将很容易让你解决这个联合问题以及过程中的向量化。 (虽然我很好奇为什么你没有isnan() 以及为什么你不能使用联合。)
  • 您可能应该在任何地方将 0x7f80000 更改为 0x7f800000
【解决方案2】:

在 cmets 中,您提到了您真正想做的事情:

在执行任何其他操作之前,我想检查 NaN 即数字!= 数字,但这不适用于发出 NaN 信号

这就是 C99 isnan() 宏的用途,不需要 number != number。 C 标准本身并没有真正指定信号 NaN 的语义 - 它只是说

a 信号 NaN 通常在作为算术操作数发生时引发浮点异常

在第 5.2.4.2.2 节 §3 和

本规范没有定义信号 NaN 的行为。

在附件 F,第 F.2.1 节第 1 节中。

然而,according to Wikipedia,IEEE 754 指定了一个 isNaN() 谓词,即使与信号 NaN 一起使用也不会引发异常,isnan() 应该在提供 IEEE 浮点点语义。

【讨论】:

猜你喜欢
  • 2023-01-20
  • 2013-04-26
  • 2018-05-19
  • 1970-01-01
  • 2019-12-26
  • 2017-06-18
  • 2016-11-26
  • 1970-01-01
  • 2011-07-04
相关资源
最近更新 更多