Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/variables/2.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_Variables - Fatal编程技术网

在C语言中使用超出其边界的变量进行计算

在C语言中使用超出其边界的变量进行计算,c,variables,C,Variables,如果我使用一个变量进行计算,其中计算的中间部分高于该变量类型的边界,是否存在一些平台可能不喜欢的危险 这是我要问的一个例子: int a, b; a=30000; b=(a*32000)/32767; 我已经编译了这个,它确实给出了29297的正确答案(无论如何,在截断错误范围内)。但让我担心的是30000*32000=960000000,这是一个30位的数字,因此不能存储在16位整数中。最终结果在整数的范围内,但我希望内存的任何工作部分都会分配与最大源变量相同的大小,因此会发生溢出错误 这只

如果我使用一个变量进行计算,其中计算的中间部分高于该变量类型的边界,是否存在一些平台可能不喜欢的危险

这是我要问的一个例子:

int a, b;
a=30000;
b=(a*32000)/32767;
我已经编译了这个,它确实给出了29297的正确答案(无论如何,在截断错误范围内)。但让我担心的是30000*32000=960000000,这是一个30位的数字,因此不能存储在16位整数中。最终结果在整数的范围内,但我希望内存的任何工作部分都会分配与最大源变量相同的大小,因此会发生溢出错误

这只是一个小例子来说明我的问题,我试图避免使用浮点,因为我使用的嵌入式系统我相信没有FPU


那么,大多数处理器如何处理超出源变量和目标变量边界的计算呢?

据我所知,大多数(如果不是所有的话)处理器都会将字*字乘法的结果保存在双字中——也就是说,8位*8位存储在8位处理器上的16位寄存器中,32位*32位操作存储在32位机器上的64位寄存器中。(至少,我使用过的所有嵌入式微控制器都是如此)


如果不是这样的话,处理器将在只允许半字*半字乘法的意义上严重受损。

AFAIK这种事情在形式上是“未定义的”。你必须做必要的代数运算来防止溢出。那永远是你的第一选择。数值稳定性不是偶然的,它需要在决定何时以及如何进行除法和乘法时小心一些

或者,您必须保证使用足够大的中间结果缓冲区

使用大的中间缓冲区是一些C编译器的工作。然而,这种语言并没有做出任何保证

所以,为了确保它有效,大多数人都会这样做

short a= 30000;
int temp= a;
int temp2= (a*32000)/32767;
// here you can check for errors; if temp2 > 32767, you have overflow.
short b= a;

有符号整数溢出是未定义的行为

几乎您可能遇到的任何实现都会围绕整数溢出展开,因为(a)每个人都使用2的补码,其中算术运算对于大小相同的有符号和无符号类型是按位相同的;(b)wrapparound是C中无符号类型的定义行为

因此,在一个具有16位
int
的实现上,我希望您的计算结果为0(如果您使用的是无符号的16位
int
,则该结果必须为0)。但我会针对它可能引发硬件异常、爆炸等的可能性编写代码


请注意,如果在一台32位
int
的机器上使用两个16位
short
变量进行计算,则通常会得到“正确”答案29297,因为中间值
(a*32000)
int
,最后只会被截断回
short
。我之所以说“一般”,是因为将越界整数值转换为有符号整数值类型要么会产生未指定的结果,要么会引发信号。但同样,您在礼貌公司遇到的任何实现都只需要一个模数。

在16位编译器/CPU上,您可以(几乎)计划给出错误结果的代码。这有点悲哀,因为几乎每个CPU(有一个乘法指令)都会生成并存储中间结果,但没有一个C编译器(我知道)会正常使用它(如果你使
a
b
无符号,它就不被允许使用它)

你有几个选择来处理这个问题。一种是用汇编语言编写一个小的
muldiv
函数,该函数进行乘法(保留高位字),然后对其进行除法,最后在将值缩小到范围内时将值返回给C


另一个选择是对无符号整数进行计算,这至少可以让您知道问题发生的时间。不幸的是,没有一个选项是我所说的特别吸引人的…

你确定你的编译器有16位整数吗?在当今的大多数系统上,整数是32位的。您没有收到错误的另一个可能原因是,一些编译器会认识到它可以在编译时计算类似的内容,并且会这样做

如果您真的担心最终会出现溢出,您有时可以对公式进行重新排序或采用不同的因子,这样中间项就不会溢出。在您的示例中,这将很难做到,因为您的所有项都接近16位值的限制。你需要数字准确无误,还是可以近似?如果可以,可以这样做:

int a, b;
a=30000;
//b=(a*32000)/32767 ~= a * (32000/32768) = a *(125/128)
b = (a / 128) * 125 // if a=30000, b = 29250 - about 0.16% error
int a, b;
a=30000;
b=((long)a*32000L)/32767L;
另一种选择是对中间术语使用较大尺寸的类型。如果编译器有16位整数和32位长整数,则可以执行以下操作:

int a, b;
a=30000;
//b=(a*32000)/32767 ~= a * (32000/32768) = a *(125/128)
b = (a / 128) * 125 // if a=30000, b = 29250 - about 0.16% error
int a, b;
a=30000;
b=((long)a*32000L)/32767L;

真的,对于如何处理溢出没有固定的答案。您需要单独评估每种情况,并确定最佳解决方案。

您的编译器和目标处理器都与各种数据类型的大小有关。 在计算过程中,编译器通常会将变量提升到最大,以便于使用,然后在最后转换赋值所需的任何大小的结果。 还有一些C规则控制升级到某些计算中更难使用的大小。如果您正在编译一个AVR,它有8位寄存器,但将int定义为16位,许多计算最终使用的寄存器比您可能认为需要的要多,因为这种升级以及代码中的常量必须被视为b