这里讨论的无符号整型指 unsigned int,32位。有符号整型指 int,32位。
在《算法竞赛进阶指南》第3页有这么一句话“发生算数溢出时,32位无符号整数相当于自动对 232 取模。
我对32有符号整数发生上溢出时,有什么特点产生了好奇,于是做了一下实验。
层级系统
我把计算机系统对整型的表示分成2个层次的系统。上层:标记系统。下层:解释系统。
上层标记系统就是用2进制对每个刻度进行标记,如32位的标记方法就是从 000…0 开始到 111…1 ,这个标记是不变的,并特提示,这个标记并不会随着编码的改变发生变化。
下层的解释系统。负责对标记进行解释,如一个32位的二进制串,到底是有符号整型、无符号整型、浮点型等等。
这样分成层级系统,容易分开理解,降低了理解难度,是我的个人想法和构思。
实验
根据上面的层级系统,可以理解,有符号系统的标记层级并未改变,故对应的二进制串不变,只不过解释系统有了不同的解释。那么有符号系统是否也可以认为溢出是自动对 232 取模呢,我觉得可以这么认为,只要代码加一点辅助。毕竟标记并未改变。
由于32系统过大,用char替代,好计算。
有符号整数正溢出时
有符号char的范围是[-128,127],非负数部分是[0,127],若有char型变量x=130,显然发生正溢出,这时结果是多少,二进制串是多少呢?
看代码:
#include <iostream>
using namespace std;
int main(){
char x=130;
printf("%d\n",x); //x输出为 -126,溢出变成负数。
return 0;
}
虽然x=130,溢出后显示为-126,但在无符号char型系统中,130和-126的二进制表示是一样的。
#include <iostream>
using namespace std;
void show(int x){
for(int i=7;i>=0;i--){
printf("%d",x>>i&1);
}
printf("\n");
}
int main(){
char x=-126;
unsigned char y=130;
show((int)x);
show((int)y);
return 0;
}
输出:
10000010
10000010
如何不相信代码,手动模拟也不麻烦,我们知道127是 01111111 ,那么-128就是10000000,-128+1=-127 10000001,-126 10000010
当我们用 char x=130 时,x的二进制被解释为 -126 ,同时也可以认为 130%256=130 ,然后解释为 -126,这个价格强转 (unsigned char)-126,立马就变130,这也可以看成,上面的层级系统的解释是正确的。
有符号整数完全溢出时
定义一个char x=400 会发生什么? x解释为 -112
#include <iostream>
using namespace std;
int main(){
char x=400;
printf("%d\n",x);
unsigned char y=400;
printf("%d\n",y);
return 0;
}
输出:
-112
144
有办法让他们的值一样吗?为了不影响结果,我同意强转为范围更大的int。
#include <iostream>
using namespace std;
int main(){
char x=400;
printf("%d\n",(int)(x+256)%256);
unsigned char y=400;
printf("%d\n",(int)(y+256)%256);
return 0;
}
输出都一样了:
144
144
可见标记系统下的二进制串始终是不变的,只不过在模256后,char得到一个负模值,unsign得到一个正模值而已。
关于负数不管模整数还是负数,结果都是负数的个人解释。
−17%5=−2
−17%−5=−2
想象一个圆,当模5时,顺时针写上0,-1,-2,-3,-4。当模-5时,逆时针写上0,-1,-2,-3,-4。你数一下会发现结果必定时 -2。这就是我个人的解释。
C++有符号数字的溢出在标准规定是ub的(undefined behaveior),这样测编译器行为没什么意义