分裂和转移之间的差异

时间:2013-01-13 10:28:22

标签: c floating-point division bit-shift

我正在阅读this问题。在阅读第一个答案后,我无法理解-5 >> 1 = -3的原因。我还对它进行了一些调整。

您还可以查看代码并输出here。 这是我做的:

#include<stdio.h>

int main(){

printf("5/2 = %d\n",5/2);
printf("5 >> 1 = %d\n",5 >> 1);
printf("5/2 = %lf\n",5/2);
printf("5 >> 1 = %f\n",5 >> 1);
printf("-5/2 = %d\n",-5/2);
printf("-5 >> 1 = %d\n",-5 >> 1);
printf("-5/2 = %f\n",-5/2);
printf("-5 >> 1 = %f\n",-5 >> 1);

return 0;
}

输出:

5/2 = 2
5 >> 1 = 2
5/2 = 2.168831
5 >> 1 = 2.168831
-5/2 = -2
-5 >> 1 = -3
-5/2 = 2.168833
-5 >> 1 = 2.168833

我无法理解5/2 == 2.168831, 5 >> 2 == 2.168831, 5 >> 1 == -3

为什么会发生这种情况? (答案很可能是基本的,我错过了一些基本的东西,所以请指导我。)

2 个答案:

答案 0 :(得分:9)

-5 / 2的结果是int,而不是floatdouble。但是,您的格式说明符为%f,因此您的int解释为float,这没有任何意义,因此不稳定的值。您正在做的事情被称为未定义的行为:任何事情都可能发生。

答案 1 :(得分:3)

您看到结果的原因是:

当您传递int参数但使用double的printf说明符时(请记住在这种情况下float转换为double),那么大多数C实现根据通常的规则传递int参数,将int参数传递给可变参数函数(接受不同参数类型的函数),但printf例程将机器状态解释为如果它传递了double参数,如下所述。 (这不一定是经常发生的事情;一旦你离开C标准定义的行为,C实现可能会做其他事情。特别是,与优化器的复杂交互会导致令人惊讶的结果。但是,这就是发生的事情。最常见的。你不能依赖它。)

每个计算平台都有一些关于如何传递参数的规则。一个平台可能指定所有参数从右到左被压入堆栈,并且每个参数仅使用所需的字节数放入堆栈。另一个平台可能会指定将参数从左向右推入堆栈,或者将参数填充到下一个四个字节的倍数,以使堆栈保持良好对齐。许多现代平台指定在一般寄存器中传递一定大小的整数参数,在浮点寄存器中传递浮点参数,并在堆栈上传递其他参数。

printf看到%f并查找double参数,但您已通过int时,printf会找到什么?如果此平台将两个参数都推送到堆栈,则printf会找到int的位,但它会将这些位解释为double。这会导致printf打印由您的int确定的值,但它与您的int没有明显的关系,因为这些位在int的编码中具有完全不同的含义, double

如果此平台在一个地方放置int参数而在另一个地方放置double参数,那么printf会找到一些与您{{1}无关的位参数。它们恰好被遗留在,例如,int参数应该在的浮点寄存器中。这些位只是以前工作的剩余部分。相对于您已通过的double,您获得的值基本上是随机的。您还可以通过int获取printf的八个字节来获取混合,其中包含您传递的double的四个字节以及附近其他任何内容的四个字节。

多次运行程序时,通常会看到打印的值相同。出现这种情况有两个原因。首先,计算机是机械的。它们以很大程度上确定的方式运行,因此它们会一遍又一遍地执行相同的操作,即使这些事情并非特别设计为按照您使用它们的方式使用。其次,每次启动程序时,操作系统启动时传递给程序的环境大致相同。其大部分内存已清除或从程序文件初始化。某些内存或其他程序状态是从计算机中的其他环境初始化的。该数据可能与程序的运行不同。例如,当前时间显然会随着运行而变化。您的命令历史记录以及命令shell在其环境变量中放置的值也是如此。有时多次运行程序会产生不同的结果。

当您使用某些规范未定义的行为的代码(可能是C规范,编译器规范,机器和操作系统规范或其他文档)时,您不能依赖该代码的行为。 (有可能依赖于由C编译器指定的特定C编译器编译的代码的行为,即使它没有被C标准完全指定。)

相关问题