将 NAN 浮点数转换为 int 的问题

Posted

技术标签:

【中文标题】将 NAN 浮点数转换为 int 的问题【英文标题】:Problems casting NAN floats to int 【发布时间】:2012-05-09 03:18:01 【问题描述】:

忽略我为什么要这样做,754 IEEE fp 标准没有定义以下行为:

float h = NAN;
printf("%x %d\n", (int)h, (int)h);

Gives: 80000000 -2147483648

基本上,无论我给出什么 NAN 值,它都会输出 80000000(十六进制)或 -2147483648(十进制)。这是否有原因和/或这是正确的行为?如果有,怎么会?

我给它不同的 NaN 值的方式在这里: How can I manually set the bit value of a float that equates to NaN?

所以基本上,是否存在 NaN 的有效负载会影响演员表输出的情况?

谢谢!

【问题讨论】:

+1 因为黑客可以更好地理解 【参考方案1】:

浮点数转换为整数的结果未定义/未指定对于不在整数变量范围内的值(±1 表示截断)。

第 6.3.1.4 条:

当实浮点类型的有限值转换为_Bool以外的整数类型时,小数部分将被丢弃(即,该值被截断为零)。如果整数部分的值不能用整数类型表示,则行为未定义。

如果实现定义了__STDC_IEC_559__,那么对于从浮点类型到除_BOOL以外的整数类型的转换:

如果浮点数为无穷大或NaN,或者如果浮点数的整数部分超出整数类型的范围,则“无效”浮点- 引发点异常,结果值未指定。

(附件 F [规范性],第 4 点。)

如果实现没有定义__STDC_IEC_559__,那么所有的赌注都没有。

【讨论】:

鉴于行为未定义这一事实,我得到的结果是这种未定义行为的常见结果吗?即是否有人知道一个系统,如果我会得到与此不同的行为? 754 规范说 NaN 操作的行为是应该携带有效载荷。 我不知道有什么不同的实现,但除了一点 gcc 之外,我不熟悉任何东西。据我所知,gcc 为所有到int 的超出范围的转换生成INT_MIN(但这也只是很少)。 我很确定你的意思是 x86 上的 gcc。没有理由假设其他地方的结果应该是相同的。这可能是 fpu 行为的产物。 哦,嗯,脸红,我当然会。但当然,非 x86 硬件是苹果为销售更多 Mac 而发明的神话。 (感谢您的更正,@R..) 我认为这是谷歌为了销售手机而发明的神话。 ;-)【参考方案2】:

首先,根据 IEEE 标准,NAN 是不被视为浮点数的所有内容。 所以它可以是几件事。在我使用的编译器中有 NAN 和 -NAN,所以它不仅仅是一个值。

其次,每个编译器都有其isnan 函数集来测试这种情况,因此程序员不必自己处理这些位。总而言之,我认为偷看价值没有任何区别。您可能会窥视其 IEEE 构造的值,例如符号、尾数和指数,但同样,每个编译器都提供了自己的函数(或者更好的说法是库)来处理它。

不过,关于您的测试,我还有更多话要说。

float h = NAN;
printf("%x %d\n", (int)h, (int)h);

您所做的转换将浮点数转换为整数。如果你想获得 浮点数表示的整数,执行以下操作

printf("%x %d\n", *(int *)&h, *(int *)&h);

也就是你取float的地址,然后把它当作指向int的指针来引用,最终取int值。这样就保留了位表示。

【讨论】:

嗨@Israel printf("%x %d\n", *(int *)&h, *(int *)&h); 这是从地址获取位表示的好方法,是否可以将位表示写回地址?说写0x7ff8000000000000到&h【参考方案3】:

这种行为是有原因的,但这不是您通常应该依赖的。

正如您所注意到的,IEEE-754 没有指定将浮点 NaN 转换为整数时会发生什么,只是它应该引发无效操作异常,您的编译器可能会忽略该异常。 C标准说行为是未定义的,这意味着你不仅不知道你会得到什么整数结果,你根本不知道你的程序会做什么;该标准允许程序中止或获得疯狂的结果或做任何事情。您可能在 Intel 处理器上执行了该程序,并且您的编译器可能使用内置指令之一进行了转换。 Intel 非常仔细地指定指令行为,将浮点 NaN 转换为 32 位整数的行为是返回 0x80000000,而不考虑 NaN 的有效负载,这是您观察到的。

因为英特尔指定了指令行为,所以如果您知道使用的指令,就可以依赖它。但是,由于编译器不向您提供此类保证,因此您不能依赖正在使用的这条指令。

【讨论】:

英特尔处理器可能会将 NAN 转换为 32 位 int 为 0x80000000,但如果您的 NAN 是编译器确定的常量值,这对您没有帮助。在这种情况下,您可能会看到 INT_MIN 以外的值,因为转换是在编译时而不是运行时完成的,因此英特尔的 x86 语义永远不会发挥作用。例如,当 GCC 在编译时将 NAN 转换为 int 时,它给出 0。 felixcloutier.com/x86/cvttsd2si 是有问题的指令。 x86 的“整数不定”值是 MSB=1,rest = 0,即 INT_MIN 或 INT64_MIN。正如您所说,指令的不同用法可能会产生不同的结果,例如x86-64 上的 float -> uint32_t 通常会转换为 int64_t 并取低半部分,因为这在 asm 中基本上是免费的,而 x86(在 AVX-512 之前)不直接提供 FP -> 无符号转换。 (C 没有定义负 FP -> 无符号的行为;模归约仅适用于宽整数类型 -> 无符号)。 如您所说,其他 ISA 可能会有所不同,例如unsigned conversion in C works as expected on x86 but not ARM

以上是关于将 NAN 浮点数转换为 int 的问题的主要内容,如果未能解决你的问题,请参考以下文章

Python如何将浮点数作为十六进制转换为十进制

C将浮点数转换为int

如何把浮点数转换成字符串?

将 INT_MAX 转换为浮点数,然后再转换回整数。

'int太大,不应该转换为浮点数'

浮点数.NaN == 浮点数.NaN