我遇到了从加法和乘法返回的浮点值最终无效的问题。
背景: 我在 Visual Studio 多线程环境中使用 Speex。在某个时刻,通常在音频编码和解码 1 或 2 分钟后,我的解码信号完全变成了 Nan。我认为我的问题与此线程( Speex on windows, audio cutting out )中讨论的问题相同,但我在这个问题上进行了更深入的研究。
情况: 我修改了 libspeex 的一部分来放置一些调试代码,这就是我所拥有的(我在这里扩展了一些宏,我知道有些部分是多余的)。
float *mem, *den; // Arrays of finite float values
float nyi; // finite float value.
float a1, a2; // debug test variables.
...
if (!_finite(mem[j]) || !_finite(mem[j+1]))
printf("Nan\n"); // Does not reach this
a1 = ((mem[j+1])+(float)(den[j])*(float)(nyi)); // a1 == expected value
mem[j] = ((mem[j+1])+(float)(den[j])*(float)(nyi)); // mem[j] == -1.#IND
a2 = ((mem[j+1])+(float)(den[j])*(float)(nyi)); // a2 == expected value
if (!_finite(mem[j]) || !_finite(mem[j+1]))
printf("Nan\n"); // Program reach this and stops at breakpoint
第一个奇怪的行为是 a1 和 a2 计算出正确的值,而 mem[j] 则没有。第二个奇怪的事情:如果我尝试重新执行对 mem[j] 语句的影响(我知道这可能会导致意外结果,但它仍然给出调试目的的提示),那么该值影响mem[j]的是预期值:与a1和a2相同。
我确实检查了显而易见的事情:
- 此代码部分受互斥锁保护:其他线程不可能损坏内存。
- 所有浮点值都是有效的、有限的,并且加法和乘法的结果应落在浮点范围内。
- 所有数组索引均在其各自数组的范围内。
如果没有其他线程运行,该问题似乎不会出现。
- 该线程:音频解码线程。
- 音频编码线程。
- 一些网络套接字线程...
它是大型软件的一部分,但解码部分确实受到适当互斥体的保护,不受其余部分的影响。
所以看起来好像在浮点计算过程中发生了上下文切换,并且之后无法恢复上下文。但很难相信会发生如此糟糕的事情。
我听说过在多线程中使用时浮点不一致,但它应该只影响最不重要的部分,而不是生成 Nan 值。
有人见过这样的行为吗?你是怎么解决的?
最佳答案
问题:
- 所有多余的 Actor 阵容是怎么回事?
den[j]
和nyi
的值是多少?
除此之外,合理的可能性是同一线程上的另一个计算要么溢出了浮点堆栈,要么使用了 MMX 指令,但在放弃控制之前未能发出 emms
指令(或者这些条件将导致原本无可争议的浮点计算产生 NaN 结果)。首先检查故障状态下的 x87 状态字,以确认或排除这些可能性。
事实上,如果没有多个线程,该问题就不会发生,这使得这种解释的可能性较小,但损坏的 x87 状态是迄今为止“否则无法解释”NaN 的最常见来源,应首先排除。
关于c - C 多线程环境中的浮点损坏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11689172/