详细介绍c语言中整数的编码规则(非常详细)

本文详细介绍了整数的有效位、无符号数编码、补码编码的原理、取值范围以及它们在C语言中的应用,包括INT_MIN的写法和数字位的扩展。通过实例展示了如何进行有符号数与无符号数之间的转换以及补码到无符号数的转换公式。
摘要由CSDN通过智能技术生成

前言

本篇博客会介绍整数的最低最高有效位,c语言中无符号数的编码,有符号数的编码,补码的使用等等等等。内容会比较多 😐,请耐心看完哦。
来源:《深入理解计算机系统》
在这里插入图片描述

有效位

假设有一个w位(bit)的数值,可以表示为 [ x w − 1 , x w − 2 , . . . . , x 1 , x 0 ] [x_{w-1},x_{w-2},....,x_1,x_0] [xw1,xw2,....,x1,x0],规定把 x w − 1 x_{w-1} xw1称为最高有效位 x 0 x_{0} x0称为最低有效位

整数4的二进制表示为100
对4而言,w = 3, 最高有效位为1 最低有效位为0

无符号数编码

下面给出无符号数编码的计算公式,其实就是二进制和十进制的互相转换。

(为了表示方便二进制串用[]括起来) 例如:
[ 1000 ] = 1 ∗ 2 3 + 0 ∗ 2 2 + 0 ∗ 2 1 + 0 ∗ 2 0 = 8 [1000] =1*2^3+0*2^2+0*2^1+0*2^0=8 [1000]=123+022+021+020=8
[ 1010 ] = 1 ∗ 2 3 + 0 ∗ 2 2 + 1 ∗ 2 1 + 0 ∗ 2 0 = 10 [1010] =1*2^3+0*2^2+1*2^1+0*2^0=10 [1010]=123+022+121+020=10
[ 1110 ] = 1 ∗ 2 3 + 1 ∗ 2 2 + 1 ∗ 2 1 + 0 ∗ 2 0 = 14 [1110] =1*2^3+1*2^2+1*2^1+0*2^0=14 [1110]=123+122+121+020=14

无符号数编码的取值范围

长度为w位的二进制串能表示值的范围为 [ 0 , 2 w − 1 ] [0,2^{w}-1] [0,2w1]。最小值用二进制串 [ 000...00 ] [000...00] [000...00]表示,最大值用二进制串 [ 111...11 ] [111...11] [111...11]表示,可以这样计算 ∑ i = 0 w − 1 2 i = 2 w − 1 \sum\limits_{{\rm{i}} = 0}^{{\rm{w - 1}}} {{2^{\rm{i}}} = {2^{\rm{w}}} - 1} i=0w12i=2w1

无符号数的二进制表示还有一个特别的属性,就是每个在 [ 0 , 2 w − 1 ] [0,2^w-1] [0,2w1]的数都有唯一一个w位数值编码。给定一个w位的数值编码,也可以唯一确定一个 [ 0 , 2 2 − 1 ] [0,2^2-1] [0,221]的数。 即:将二进制数转为无符号数的函数是双射函数。
在这里插入图片描述

补码

上面的无符号数有一个明显的缺点,就是只能表示正数,但是在很多情况下我们希望能够表示负数,于是我们可以使用补码编码。

补码编码

与无符号数编码不同的是,补码编码最高有效位为符号位,权重为 − 2 w − 1 -2^{w-1} 2w1

  • 当符号位为1时,其值为 − 2 w − 1 -2^{w-1} 2w1
  • 当符号位为0时,其值为0

示例:
[ 0001 ] = − 0 ∗ 2 3 + 0 ∗ 2 2 + 0 ∗ 2 1 + 1 ∗ 2 0 = 1 [0001]=-0*2^3+0*2^2+0*2^1+1*2^0 =1 [0001]=023+022+021+120=1
[ 1011 ] = − 1 ∗ 2 3 + 0 ∗ 2 2 + 1 ∗ 2 1 + 1 ∗ 2 0 = − 5 [1011]=-1*2^3+0*2^2+1*2^1+1*2^0 =-5 [1011]=123+022+121+120=5

补码编码的取值范围

w位补码的取值范围为 [ − 2 w − 1 , 2 w − 1 − 1 ] [-2^{w-1},2^{w-1}-1] [2w1,2w11]最小值的二进制串为 [ 1000...000 ] [1000...000] [1000...000](因为只有最高有效位用于表示负,其它位都表示正)。最大值的二进制串为 [ 0111...111 ] [0111...111] [0111...111]

int类型w为32,可以由此推出int的取值范围 [ − 2 31 , 2 31 − 1 ] [-2^{31},2^{31}-1] [231,2311] ,即 [ − 2147483648 , 2147483547 ] [-2147483648,2147483547] [2147483648,2147483547]

从补码转换到二进制的函数是一个双射函数

c语言的有符号数

在大部分机器上,c语言都会使用补码来表示有符号整数
大多数数字也是默认有符号的,要创建一个无符号常量,必须加上后缀字符U或者u

如:1234U
0x1234u

补码的使用

对于非负数x,我们用 2 w − x 2^w-x 2wx做为-x的补码表示

举个例子:
short x = 12345;
short mx = -x; // mx为-12345

2 w 2^w 2w的二进制表示为 1 00000000 00000000
x的二进制表示为 00110000 00111001
2 w − x 2^w-x 2wx的二进制表示为 11001111 11000111
结果为 1+2+4+64+128+256+512+1024+2048+16384-32768 = -12345


有符号数的另外两种表示方法

反码

最高有效位权重为 − ( 2 w − 1 − 1 ) -(2^{w-1}-1) (2w11),其余和补码一样

原码

最高有效位作为符号位,权重为 − 1 -1 1,其余和补码一样

有符号数和无符号数之间的转化

先介绍转化的规则

不改变位值,而是改变解释位的方式

在这里插入图片描述
分析一下上图的步骤

  1. 将有符号数转换为对应位值:-12345的补码编码为 [ 1100111111000111 ] [11001111 11000111] [1100111111000111]
  2. 将该位值使用无符号数编码,结果为53191

53191 = 1 + 2 + 4 + 64 + 128 + 256 + 512 + 1024 + 2048 + 16384 + 32768 53191 =1+2+4+64+128+256+512+1024+2048+16384+32768 53191=1+2+4+64+128+256+512+1024+2048+16384+32768

[11001111 11000111]的补码和无符号数编码如下:
-12345=1+2+4+64+128+256+512+1024+2048+16384 -32768
53191 =1+2+4+64+128+256+512+1024+2048+16384 +32768
当补码编码为负数时,设[0 ~ w-2]位的数值为A
补码对应值: A − 2 w − 1 A-2^{w-1} A2w1
无符号数对应值: A + 2 w − 1 A+2^{w-1} A+2w1
相互转换只需 ± 2 w \pm2^w ±2w

补码到无符号数之间的转换公式

根据上面的推导,可以推出公式
补码 x 变成无符号数 = { x + 2 w , x < 0 x , x ≥ 0 补码x变成无符号数=\left\{ \begin{array}{l}x + {2^w},x < 0\\x,x \ge 0\end{array} \right. 补码x变成无符号数={x+2w,x<0x,x0
相应地,可以推出无符号数变成补码的公式
无符号数 u 变成补码 = − u w − 1 2 w + u 无符号数u变成补码 = -u_{w-1}2^w+u 无符号数u变成补码=uw12w+u
u w − 1 为 u 的最高有效位 u_{w-1}为u的最高有效位 uw1u的最高有效位

什么时候会发生符号数和无符号数之间的转化

  1. 强制类型转换,如下面的代码
int x;
unsigned int ux =(unsigned int)x;
  1. 将一种类型的变量赋值给另外一种类型的变量时
int x;
unsigned int ux;
ux = x;
  1. 当有符号无符号数参与运算时,c语言会隐式的将有符号数转化为无符号数
#include <iostream>
int main()
{
    if (-1 > 0U)
    {
        std::cout << "有符号数被转换为无符号数";
    }
    return 0;
} //结果成功输出,大家可以自行验证一下

c语言中INT_MIN的写法

在c语言的<limits.h>头文件里定义了一组常量,INT_MAXINT_MIN确定了有符号整数的取值范围。
观察源码,发现INT_MIN的定义有些奇怪

#define INT_MAX 2147483637
#define INT_MIN (-INT_MAX-1) 

为什么不直接写成-2147483648呢?原因如下

  1. 编译器在遇到形如-x的表达式时,会先确定x的数据类型和值
  2. 2147483648( 2 31 2^{31} 231)超出int的范围(int最高位为符号位,int最大值为 2 31 − 1 2^{31}-1 2311)
  3. 编译器会尝试找到一个数据类型可以正确表示这个值。不同标准的c找到的类型不同。在32位机器上的结果如下。
  • 对于ISO C90版本,编译器从intlong再到 unsigned, 最终找到的数据类型是unsigned,值为2147483648。
  • 对于ISO C99版本,编译器从intlong再到 long long ,它最终找到的数据类型是long long,值为-2147483648。

为什么将2147483648扩展到long long类型,值会变成-2147483648呢,在这就要介绍如何扩展数字的位了。

扩展数字位的表示

有时候,我们会将一个数据类型转化到更大的数字类型。

无符号数的扩展

将一个无符号数转化为更大的数据类型,只要在开头加上0就行了,这被称为无符号数的0扩展

有符号数的扩展

我们希望扩展之后数依然不变

先观察个简单的例子
[ 101 ] [101] [101] ,w为3 表示值-4+1=3
[ 1101 ] [1101] [1101] ,w为4 表示值-8+4+1=3
[ 11101 ] [11101] [11101],w为5 表示值 -16+8+4+1=3

可以得出结论,因为篇幅问题这里就不给出证明了。主要是懒
[ x w − 1 , . . . , x w − 1 ⏟ k 次 , x w − 1 , x w − 2 , . . . , x 0 ] = [ x w − 1 , x w − 2 , . . . , x 0 ] [\underbrace {{x_{w - 1}},...,{x_{w - 1}}}_{k次},{x_{w - 1}},{x_{w - 2}},...,{x_0}] = [{x_{w - 1}},{x_{w - 2}},...,{x_0}] [k xw1,...,xw1,xw1,xw2,...,x0]=[xw1,xw2,...,x0]

这就能解释为什么将2147483648扩展到long long类型,值会变成-2147483648

  1. 2147483648的二进制表达式 [ 10000000 00000000 00000000 00000000 ] [10000000 \quad 00000000\quad 00000000 \quad00000000] [10000000000000000000000000000000]
  2. 这个数字超出int的表示范围了,所以在C99下被扩展为long long格式,w被扩充到64位。
  3. 由于最高有效位是1,所以用1来补齐缺少的位。得到的64位值为 [ 11111111 11111111 11111111 11111111 10000000 00000000 00000000 00000000 ] [11111111\quad11111111\quad11111111\quad11111111\quad10000000\quad 00000000\quad 00000000 \quad 00000000] [1111111111111111111111111111111110000000000000000000000000000000]
  4. 其值就是-2147483648

本文结束

看完本文的样子 be like :
在这里插入图片描述
不会连这篇文章都看不懂吧,真是
在这里插入图片描述
然后被暴打

一些废话

花了两天时间把这篇博客整理出来。。。。 虽然大一自学c的时候也有学过,不过当时感觉很难就只没怎么仔细看。现在看来其实没那么难。

题外话:写算法题的时候数据过大会爆int , 所以用long long 应该是个好习惯吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值