Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 函数参数中的舍入错误_C_Linux_Ubuntu - Fatal编程技术网

C 函数参数中的舍入错误

C 函数参数中的舍入错误,c,linux,ubuntu,C,Linux,Ubuntu,我对这个模棱两可的标题表示歉意 今天我重构了一些非常旧的(c89旧的)C代码,遇到了一个非常奇怪的取整问题 旧代码使用了一组#defines来声明特定计算中使用的一些值。在重构代码的过程中,我打算将计算包装成更可重用的函数,该函数接受值作为参数,但是在Ubuntu下我遇到了一个相当奇怪的舍入问题(在Windows上不是这样) 用一个例子来解释可能更容易: #include <stdio.h> #define SOMEVAR 0.001 void test(double val

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

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

旧代码使用了一组
#define
s来声明特定计算中使用的一些值。在重构代码的过程中,我打算将计算包装成更可重用的函数,该函数接受值作为参数,但是在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);  
}
我知道浮点运算会有精度损失,但这肯定是一个可以解决的问题吗?我真的很想将计算封装到一个可重用的函数中,但是由于从
#define
s切换到函数参数的精度降低,所有计算都将是不正确的

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

#define DT  0.001
// -- snip

int steps = (int)(1.0 / DT); // evaluates to 1000
for(int i = 0; i < steps; ++i) 
    // do stuff
#定义DT 0.001
//--剪断
整数步长=(整数)(1.0/DT);//估计为1000
对于(int i=0;i
vs

void计算(双dt){
int steps=(int)(1.0/dt);//计算结果为999
对于(int i=0;i
如您所见,函数化版本的迭代次数将比
#define
版本少一次,这意味着结果永远不会匹配

还有谁遇到过这个问题吗?有解决办法吗?或者我应该停止与
#define
斗争,接受它并解决它吗


<>编辑:这是在使用<代码> GCC < /C>或<代码> G++< /C> >(我的重构版本将用C++编写,而不是C99 C,我只是在这个例子中使用C来简化)。我们可以看到,第一次计算归结为一个常数:

movl    $1000, %esi #,
因此,在本例中,编译器在转换过程中执行计算,因为这两个值都是常量,并且它知道表达式实际上只是:

1.0 / 0.001
而在第二种情况下,由于两个值都不是常量,编译器在运行时进行计算:

divsd   %xmm0, %xmm1    # value, D.1987
cvttsd2si   %xmm1, %esi # D.1987, calc2
因此,不幸的是,计算结果并不相等,在某些情况下可能会导致不同的结果,尽管我还不能重现您在任何在线编译器上看到的结果

<>如果你要重构C++,可以使用C++ 11,那么你可以总是使用<代码> CONTXPRPR <代码>来获得编译时间评估:

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

您特别询问C和C89;请不要用C++来双重标记这样的问题。它在C和C++两种情况下发生,我只说旧版本的代码是代码> C89<代码>,我的修订版本将在C++中,在Ubuntu下也有同样的舍入问题。为什么?C++和C++都是一个合法的问题,旧版本是用旧的C语言编写的,我的C++将用C++编写。如果有一个代码> C++ +<代码> -具体的解决方案,如果我只在标签中指定了代码>代码>代码>,我将永远不会得到它。@ JONAATEN LeFFLLE如果OP重构到C++,那么我认为添加C++标签是有意义的。这是否意味着我已经完蛋了,需要接受它并使用
#define
s进行编译时评估?@JasonLarke我无法重现结果,因此很难提出解决方案,因为我无法验证它们。您在每个平台上使用的编译器和版本是什么?
gcc(Ubuntu/Linaro 4.6.3-1ubuntu5)4.6.3
g++(Ubuntu/Linaro 4.6.3-1ubuntu5)4.6.3
Ubuntu 12.04.3 LTS
(作为VirtualBox虚拟机运行)还有
constepr
,是否有任何方法向编译器提示函数参数?i、 e
void test(constexpr double x)
?@JasonLarke您可以使用,但参数必须是常量表达式,以便在编译时对其求值。
divsd   %xmm0, %xmm1    # value, D.1987
cvttsd2si   %xmm1, %esi # D.1987, calc2
constexpr double SOMEVAR  = 0.001 ;
//....
constexpr int calc1 = (int)(1.0 / SOMEVAR );