gpt4 book ai didi

c - 函数参数中的舍入错误

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:53:11 25 4
gpt4 key购买 nike

我对这个非常模棱两可的标题表示歉意。

我今天重构了一些非常旧的(c89 旧的)C 代码,遇到了一个非常奇怪的舍入问题。

旧代码使用一堆 #define 来声明特定计算中使用的一些值。在重构代码时,我打算将计算包装到更可重用的函数中,该函数接受值作为参数,但是我在 Ubuntu 下遇到了一个相当奇怪的舍入问题(在 Windows 上不是这种情况)。

用一个例子可能更容易解释:

#include <stdio.h>

#define SOMEVAR 0.001

void test(double value) {
int calc1 = (int)(1.0 / SOMEVAR); // using the #define directly (so an in-place 0.001)
int calc2 = (int)(1.0 / value); // using the parameter value

printf("#define: %d\n", calc1); // prints 1000, as expected
printf("param: %d\n", calc2); // prints 999 on Ubuntu and 1000 on Windows
}

int main(int argc, char *argv[]) {
test(SOMEVAR);
}

在 Ubuntu 上使用以下命令编译

gcc -std=c99 -o test test.c

我知道在涉及浮点运算时会出现精度损失,但这肯定是一个可以解决的问题吗?我真的很想将计算封装到一个可重用的函数中,但是由于从 #define 切换到函数参数时精度下降,所有计算都会不正确。

作为我的意思的一个例子,这里摘录了代码中的一点,这会产生巨大的差异:

#define DT  0.001
// -- snip

int steps = (int)(1.0 / DT); // evaluates to 1000
for(int i = 0; i < steps; ++i)
// do stuff

对比

void calculate(double dt) {
int steps = (int)(1.0 / dt); // evaluates to 999
for(int i = 0; i < steps; ++i)
// do stuff
}

如您所见,函数化版本将比 #define 版本少迭代一次,这意味着结果永远不会匹配。

有没有人遇到过这个问题?有没有解决方案,或者我应该停止与 #define 作斗争,接受它并解决它?

编辑:当使用 gccg++ 时会发生这种情况(我的重构版本将用 C++ 编写,而不是 c99 C,为了简单起见,我在这个例子中只使用了 C ).

最佳答案

如果我们查看 godbolt output,这看起来像是在不断折叠对于您的第一组代码,我们可以看到第一个计算被归结为一个常数:

movl    $1000, %esi #,

所以在这个例子中,编译器在翻译过程中执行计算,因为两个值都是常量,并且它知道表达式实际上只是:

1.0 / 0.001

而在第二种情况下,由于两个值都不是常量,编译器会在运行时求值:

divsd   %xmm0, %xmm1    # value, D.1987
cvttsd2si %xmm1, %esi # D.1987, calc2

因此不幸的是,计算并不相同,在某些情况下可能会导致不同的结果,尽管我还不能重现您在任何在线编译器上看到的结果。

如果您要重构为 C++ 并且可以使用 C++11,那么您始终可以使用 constexpr 进行编译时评估:

constexpr double SOMEVAR  = 0.001 ;
//....
constexpr int calc1 = (int)(1.0 / SOMEVAR );

关于c - 函数参数中的舍入错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26029645/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com