繁体   English   中英

c中的变量如何存储数学运算?

[英]How mathematical operations are stored in variables in c?

我一直在使用 Computer Systems: A Programmer's Perspective 来了解有关计算机体系结构的更多信息。 今天,我正在研究无符号和有符号的加法以及这两种情况下的溢出处理。

我对无符号加法没有问题,所以有符号加法不是问题,因为它基本上是无符号加法,从有符号到无符号和无符号到有符号的转换,在书中也有说明:

两个数字的 w 位二进制补码和具有与无符号和完全相同的位级表示。 事实上,大多数计算机使用相同的机器指令来执行无符号或有符号加法。

同样用公式,其中 w 代表字长:


从这里我开始练习,这个练习有一个错误的代码,我需要分析

int tadd_ok(int x, int y) {
    int sum = x+y;
    return (sum-x == y) && (sum-y == x);
}

在答案中,它说sum-x == y中的操作sum-x导致形成一个阿贝尔群,因此它变成x+yx并且无论总和值如何,y 都会留下,因此,导致不正确的评价。

所以,这就是我想知道的事情:我一直直觉地认为,值将在对变量进行操作之后分配,而不是操作本身,因为如果不是这种情况,变量sum将只有x+y的值x+y ,不是操作。 然后它会在后一个操作中导致类似于some_value-x的东西不会导致形成阿贝尔群,因此它会按预期运行。 相反,它将操作本身带到了sum-x == y

发生这种情况是因为x+y值由于大小而不能存储在 int 中,所以它只是进行操作,还是它总是这样工作,即使没有潜在的溢出? 如果是这样,其他所有编程语言是否总是如此?

为了这:

int tadd_ok(int x, int y) {
    int sum = x+y;
    return (sum-x == y) && (sum-y == x);
}

您所期望的是,对于xy的某些值,相加会导致溢出。 对于有符号数,C 不假设有符号数的表示方式(例如,实现可能使用“二进制补码”,也可能不会),并且有符号的 integer 溢出是未定义的行为(可能会环绕,可能会格式化您的硬盘驱动器)。 因为溢出是未定义的行为,编译器可以做任何它喜欢的事情,包括假设它永远不会发生(这是一些编译器已知的事情)。

如果编译器确实假设未定义的行为永远不会发生( x+y永远不会溢出),那么编译器也可以假设(sum-x == y) && (sum-y == x)始终为真,并且编译器可以将代码优化成:

int tadd_ok(int x, int y) {
    return true;
}

在答案中,它说 sum-x == y 中的操作 sum-x 导致形成一个阿贝尔群,因此它变成 x+yx 并且无论总和值如何,y 都会留下,因此,导致不正确的评价。

那是“在实践中可能实现的”(如果实现对有符号整数使用“双恭维”表示,并且如果“未定义的行为”变为包装/截断)但不能保证,即使底层 CPU 使用“双恭维”和包装/截断。

所以,这就是我想知道的事情:我一直直觉地认为,值将在对变量进行操作之后分配,而不是操作本身,因为如果不是这种情况,变量 sum 将只有 x 的值+y,不是操作。 然后它会在后一个操作中导致类似于 some_value-x 的东西不会导致形成阿贝尔群,因此它会按预期运行。 相反,它将操作本身带到了 sum-x == y

对于“在实践中可能(但不保证)”; 该值已分配。 例如,对于 16 位int ,使用tadd_ok(30000, 30000)之类的sum = x + y; 会做sum = 30000 + 30000 = +60000 = too big to fit = -5536 due to wrapping ,然后sum - x = -5536 - 30000 = -35536 = too big to fit = +30000 due to wrapping = y

发生这种情况是因为 x+y 值由于大小而不能存储在 int 中,所以它只是进行操作,还是它总是这样工作,即使没有潜在的溢出?

如果发生这样的事情; 然后它发生是因为第一次溢出(在sum = x + y;中)导致换行,然后是第二次溢出(在sum - x中)和第三次溢出(在sum - y中)导致更多的换行,从而抵消了造成的换行通过第一次溢出。

如果是这样,其他所有编程语言是否总是如此?

C 并非总是如此。 例如,对于有符号整数,使用“符号和大小”表示的 16 位int ,例如tadd_ok(30000, 30000) sum = x + y; 可以做sum = 30000 + 30000 = +60000 = too big to fit = +27232 due to wrapping of the magnitude part only然后sum - x = 27232 - 30000 = -2768 != y 当然在这种情况下(因为它是未定义的行为)编译器仍然可以始终返回 true (如开头所述); 并且相同的代码可能以一种方式(禁用优化器)和另一种方式(启用优化器)运行。

一般来说; “二进制补码”更为常见,并且(出于性能原因)大多数语言不会对溢出做任何事情; 所以(没有优化)“在实践中可能(但不能保证)”的行为在其他语言中并不少见。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM