-1

我编写了一个使用 IIR 滤波器和直方图执行数学计算的代码。该代码应该是 100% 可移植的,是可重入的,不进行系统调用(但 memset),具有双精度常量和初始化变量,并且仅包括 math.h、string.h、stdint.h 和 stddef.h。它已在嵌入式处理器中运行。

使用 Windows MinGW GCC 或 Borland C++ 编译和运行代码时,它将通过所有单元测试。这不会发生在 Linux64 GCC 平台上。进一步的调查表明,该算法变得轻微不稳定,输出值不会收敛到稳定的结果,相反,结果会慢慢趋于无穷大,因此其中一个测试会失败。

大多数代码使用双精度浮点数,在某些变量中也使用单精度。

我需要关于如何解决这个问题的帮助,我必须保证代码的完全可移植性,而且我不知道去哪里找。另外,代码足够大,可以在这里发布,所以,如果你能指出我会遵循的方向。

这是 Linux 上模块的编译行:

gcc -O3 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/flickerModule.d" -MT"src/flickerModule.d" -o "src/flickerModule.o" "../src/flickerModule.c"

这是 Windows 上模块的编译行:

gcc -O0 -g3 -Wall -c -fmessage-length=0 -o src\flickerModule.o ..\src\flickerModule.c

这里我展示了代码的所有依赖:

fanl@fanl-STI:~/WorkFelipe/Codigos/re7k_eclipsewkspace/flicker_unittest/src$ grep -H -n -E 'mem|sqrt|floor' flickerModule.c
flickerModule.c:76: memset(filt, 0x00, sizeof(struct s_filter));
flickerModule.c:81: memset(filt, 0x00, sizeof(struct s_filter));
flickerModule.c:89: memset(&histo->buf, 0x00, sizeof(uint32_t) * NUM_CLASSES);
flickerModule.c:162:    return (floorf(x + 0.5));
flickerModule.c:189:            memset(&histn, 0x00, sizeof(float) * NUM_CLASSES);
flickerModule.c:262:        hp->Urms_meio_ciclo = sqrt(hp->Acc_Urms_meio_ciclo / NUM_AMOSTRAS_MEIO_CICLO_60Hz);
flickerModule.c:370:        return (sqrtf(saida));
flickerModule.c:393:        p->GanhoNormalizaEntradaFlickerMeter = 1.0 / (p->halfPeriod.Urms_meio_ciclo * sqrt(2));
flickerModule.c:419:            dbg->Prms = sqrt(dbg->Acc_Prms / (NUM_AMOSTRAS_1MIN / FATOR_DOWN1));
4

3 回答 3

3

您的 linux 构建使用 -O3(大量优化),而您的 Windows 构建使用 -O0(无优化)。优化浮点代码真的很困难,并且经常在准确性和速度之间进行权衡。尝试在 linux 构建上使用 -O0 。这里有一篇关于VS编译器优化浮点代码的一些问题的文章,gcc编译器也会面临类似的问题。

在 IA32 上,FPU 在内部以 80 位精度工作,无论浮点数还是双精度数,当数据写入 RAM 时,如果从 80 位减小到 32/64 位,精度就会丢失。您还可以修改超越函数的精度,但这是程序员的选项,通常设置为最高精度。

一些优化器将使用 SSE 寄存器来操作浮点数和双精度数,它们在内部以 32 位和 64 位精度工作。优点是优化器可以向量化代码并并行执行操作,SSE 中有几个功能不在 FPU 中,而且 SSE 的编程模型(CPU 样式寄存器)比FPU(基于堆栈的寄存器)。不利的一面是,由于精度降低,结果可能与使用 FPU 获得的结果略有不同。

所以尝试使用调试构建选项编译这两个版本,看看那里是否有任何区别。

如果您希望它们完全一样地工作,您可能需要考虑手工制作浮点代码以消除编译器的任何特性。

于 2013-07-04T20:51:10.640 回答
1

您是否尝试过使用 LDRA/Lint/等工具运行静态分析?像这样的工具通常可以查明关注的领域,您的代码是否在打开最大警告的情况下编译而不给出任何警告。编译器通常也会突出显示潜在的问题。就我个人而言,我发现在 gcc 中使用 -Wall -Werror 是一种很好的做法。

于 2013-07-04T19:31:29.590 回答
0

(注意:这不是答案,但我需要格式化)。您可以尝试用sizeof (type)相应的sizeof expr版本替换所有行。这不是灵丹妙药,而更像是一种风格提示,从而导致更健壮的代码。首先,您可以更改:

memset(filt, 0x00, sizeof(struct s_filter));

memset(&histo->buf, 0x00, sizeof(uint32_t) * NUM_CLASSES);

memset(&histn, 0x00, sizeof(float) * NUM_CLASSES);

进入:

memset(filt,0, sizeof *filt);

memset(&histo->buf, 0, sizeof histo->buf * NUM_CLASSES);
return (floorf(x + 0.5));
memset(&histn, 0, sizeof histn * NUM_CLASSES);

假设这可能是错误的,因为我不知道实际大小,因为您没有显示结构定义。顺便说一句:您不需要十六进制常量0x00;无论如何它只是零。

更新:只是为了排除事情(arrest the ususal suspects::鉴别诊断):尝试在单个线程中测试代码。不同平台的线程机制可能不同。

于 2013-07-04T20:48:20.503 回答