c ++ fmod为fmod返回NaN(0.0,1.0)

时间:2016-06-03 13:00:01

标签: c++

我遇到了一个非常奇怪的情况: 在我正在开发的多线程应用中,在某个代码点fmod(x,y)会在-1.#IND00double x = 0.0时返回double y = 1.0。当然,我仔细检查了xy的值。更奇怪的是,如果我在f=fmod(0.0,1.0);行之前添加虚拟测试代码行f=fmod(x,y);,则fmod(x,y)会按预期返回0.0

该应用程序使用多线程运行时库构建。 (VS2005)可能是什么原因?

编辑1: 经过几个小时的进一步研究,我发现它与FPU状态寄存器有关。设置零除异常标志后,它返回-1。#IND00否则返回正确的0.0值。我仍然不知道这里发生了什么。我的代码并没有弄乱FPU状态寄存器,但它是关于插件的,所以我无法控制插件主机正在做什么。但即使设置了零分割异常标志,为什么这在fmod denom值= 1.0时会产生差异。这是fmod实现中的错误吗?有人听说过这样的错误吗?这让我疯了。

编辑2: 我很确定这是VS2005中CRT lib中的64位fmod实现中的一个错误。当调用fmod时已经引发FPU状态寄存器DivByZero标志,这会产生错误的结果。我喜欢100%通过一些测试代码证明这一点,但我不能用VS2005进行x64内联汇编以强制用FPU除以零,这将设置该标志,然后我调用fmod。

2 个答案:

答案 0 :(得分:3)

我可以确认这是fmod的VS2005 CRT 64位实现中的一个错误。它很容易重复:确保在调用z = fmod(x,y)之前提升FPU中的DivByZero标志,其中x = 0.0且y = 1.0,那么z将是NaN,当然这是错误的。

因此,这与多线程无关,也与内存损坏无关。它只是CRT lib中的一个错误。

我使用VirtualAlloc做了一个小测试应用程序,有一些可执行内存,我把代码字节放在FPU中除以零,以便引发FPU DivByZero标志。 (VS2005不能内联64位汇编代码因此exe ram技巧)然后我执行上面提到的fmod函数调用,实际上z显示为-1。#IND00 = NaN。所有步骤都在调试器中进行了双重检查。 QED。

我很惊讶我在www上找不到关于这个CRT错误的任何信息。我是第一个发现这个CRT错误的人吗?

无论如何,问题已得到解答。结论:我将为fmod使用替代实现。

PS:如果有人想重复测试,下面是我用来在FPU中强制提升DivByZero标志的x86代码字节:

0xD9,0xEE = fldz
0xD9,0xE8 = fld1
0xD8,0xF1 = fdiv ST(0),ST(1)
0xDD,0xD8 = fstp ST(0) = pop stack
0xDD,0xD8 = fstp ST(0) = pop stack
0xC3 = ret

答案 1 :(得分:1)

如果使用常量参数,则编译器可能会对该值进行硬编码。所以这段代码实际上并没有调用fmod:

f=fmod(0.0,1.0);

而是分配一个常量浮点值。这解释了为什么您的虚拟线计算正确的结果。 似乎是标准/数学库的问题。