STM32F407 CORTEX-M4 上的浮点数 NaN 比较失败
Failing comparison of floats NaNs on STM32F407 CORTEX-M4
我正在尝试比较从 nanf("1")
返回的两个浮点数,但程序没有进入 if
块。
int main(void)
{
volatile float f;
volatile float ff;
uint32_t* view1;
uint32_t* view2;
view1 = ((uint32_t*)&f);
view2 = ((uint32_t*)&ff);
f = nanf("1");
ff = nanf("1");
if(f == ff)
{
f = 0;
}
while(1);
}
调试器显示 f
和 ff
(通过 view1
和 view2
)变量具有安静的 NaN 值 (QNaN == 0x7FC00001
)。
编译器:
gcc version 4.8.3 20131129 (release) [ARM/embedded-4_8-branch revision
205641] (GNU Tools for ARM Embedded Processors)
编译器标志:
-mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16
-std=c99 -DSTM32F407VETx -DSTM32 -DSTM32F4 -DDEBUG
-O0 -g3 -Wall -fmessage-length=0 -ffunction-sections -c
链接器的标志:
-mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16
-Wl,--gc-sections -lm
这是输出程序集(不能将其粘贴到代码块中):
if(f == ff)
080129b4: ldr r3, [pc, #32] ; (0x80129d8 <main+92>)
080129b6: vldr s14, [r3]
080129ba: ldr r3, [pc, #36] ; (0x80129e0 <main+100>)
080129bc: vldr s15, [r3]
080129c0: vcmp.f32 s14, s15
080129c4: vmrs APSR_nzcv, fpscr
080129c8: bne.n 0x80129d2 <main+86>
f = 0;
080129ca: ldr r3, [pc, #12] ; (0x80129d8 <main+92>)
080129cc: mov.w r2, #0
080129d0: str r2, [r3, #0]
while(1);
080129d2: b.n 0x80129d2 <main+86>
当比较数字时,当且仅当两个操作数是相等的数字时,==
产生真 (1)。 NaN 不是数字,因此它们永远不可能是相等的数字。每当 x
或 y
是 NaN 时,x == y
总是产生假 (0),即使它们是相同的 NaN。
如果需要判断两个NaN是否相同,可以比较它们表示的字节:
if (memcmp(&f, &ff, sizeof f) == 0)
备注
memcmp
在 <string.h>
中声明。
- 您的代码
view1 = ((uint32_t*)&f);
表明您使用 *view1
将 f
的字节作为 uint32_t
检查。不要这样做,因为 C 标准不支持它,并且有简单的支持方法。要将 f
的字节作为 uint32_t
检查,您可以使用 uint32_t view1; memcpy(&view1, &f, sizeof view1));
或 uint32_t view1 = (union { float x; uint32_t y; }) { f } .y;
.
volatile
不是必需的,尤其是在您停止不支持的别名后。如果 f
和 ff
是 volatile
,那么 memcmp
需要转换:memcmp((void *) &f, (void *) &ff, sizeof f)
.
- 如果
float
类型具有填充位,则以这种方式进行比较可能会产生漏报。 (语义上相同的两个 NaN 可能具有不同的填充位。)但是,在现代 C 实现中,这即使不是不存在,也是不常见的。由于您的目标是使用 32 位浮点类型的特定实现,因此如果其 C 类型为 32 位,则它不能具有填充位。
我正在尝试比较从 nanf("1")
返回的两个浮点数,但程序没有进入 if
块。
int main(void)
{
volatile float f;
volatile float ff;
uint32_t* view1;
uint32_t* view2;
view1 = ((uint32_t*)&f);
view2 = ((uint32_t*)&ff);
f = nanf("1");
ff = nanf("1");
if(f == ff)
{
f = 0;
}
while(1);
}
调试器显示 f
和 ff
(通过 view1
和 view2
)变量具有安静的 NaN 值 (QNaN == 0x7FC00001
)。
编译器:
gcc version 4.8.3 20131129 (release) [ARM/embedded-4_8-branch revision 205641] (GNU Tools for ARM Embedded Processors)
编译器标志:
-mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -std=c99 -DSTM32F407VETx -DSTM32 -DSTM32F4 -DDEBUG -O0 -g3 -Wall -fmessage-length=0 -ffunction-sections -c
链接器的标志:
-mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -Wl,--gc-sections -lm
这是输出程序集(不能将其粘贴到代码块中):
if(f == ff)
080129b4: ldr r3, [pc, #32] ; (0x80129d8 <main+92>)
080129b6: vldr s14, [r3]
080129ba: ldr r3, [pc, #36] ; (0x80129e0 <main+100>)
080129bc: vldr s15, [r3]
080129c0: vcmp.f32 s14, s15
080129c4: vmrs APSR_nzcv, fpscr
080129c8: bne.n 0x80129d2 <main+86>
f = 0;
080129ca: ldr r3, [pc, #12] ; (0x80129d8 <main+92>)
080129cc: mov.w r2, #0
080129d0: str r2, [r3, #0]
while(1);
080129d2: b.n 0x80129d2 <main+86>
当比较数字时,当且仅当两个操作数是相等的数字时,==
产生真 (1)。 NaN 不是数字,因此它们永远不可能是相等的数字。每当 x
或 y
是 NaN 时,x == y
总是产生假 (0),即使它们是相同的 NaN。
如果需要判断两个NaN是否相同,可以比较它们表示的字节:
if (memcmp(&f, &ff, sizeof f) == 0)
备注
memcmp
在<string.h>
中声明。- 您的代码
view1 = ((uint32_t*)&f);
表明您使用*view1
将f
的字节作为uint32_t
检查。不要这样做,因为 C 标准不支持它,并且有简单的支持方法。要将f
的字节作为uint32_t
检查,您可以使用uint32_t view1; memcpy(&view1, &f, sizeof view1));
或uint32_t view1 = (union { float x; uint32_t y; }) { f } .y;
. volatile
不是必需的,尤其是在您停止不支持的别名后。如果f
和ff
是volatile
,那么memcmp
需要转换:memcmp((void *) &f, (void *) &ff, sizeof f)
.- 如果
float
类型具有填充位,则以这种方式进行比较可能会产生漏报。 (语义上相同的两个 NaN 可能具有不同的填充位。)但是,在现代 C 实现中,这即使不是不存在,也是不常见的。由于您的目标是使用 32 位浮点类型的特定实现,因此如果其 C 类型为 32 位,则它不能具有填充位。