CSAPP笔记:Lecture 02 Bits, Bytes and Integer

位移操作

  1. 二进制优势在于容易表示、抗干扰等,在表示模拟信号的时候也有优势。
  2. 运算符
    1. &, |, !
    2. &&, ||, !!
    3. >>, << :位移运算又分为逻辑位移、算术位移, 其中理解算数右移需要理解计算机内如何表示负数.

位移实验

移动的位数等于int的位数(4bytes * 8 = 32bis),结果不变。

weiyi1

如果是31位的话:

weiyi2

书中的描述:在许多机器上,当移动一个 w w w 位的值时,移位指令只考虑位移量的低 l o g 2 w log_2w log2w 位,因此实际上位移量是通过计算 k    m o d    w k\;mod\;w kmodw得到的,这里的 k k k是程序写的位移量。所以位移32位,32 mod 32 = 0,31 mod 32 = 31,因此前者相当于没位移(结果不变,是1012),后者位移了31位。

这个思路下,如果 x = 1 x = 1 x=1,也就是 00....1 00....1 00....1,位移31位后,是 100...0 100...0 100...0,那它应该是 M i n i a m l I n t e g e r MiniamlInteger MiniamlInteger的值。

weiyi3

B 2 U ( U n s i g n e d ) B2U(Unsigned) B2U(Unsigned) B 2 T ( T w o ′ s c o m p l e m e n t ) B2T(Two's complement) B2T(Twoscomplement) 有专门的计算公式. 前者是无符号数, 后者是补码.

二进制转无符号数是把对应比特位的2的幂累加.
B 2 U ( X ) = ∑ i = 0 w − 1 x i ⋅ 2 i B2U(X) = \sum_{i=0}^{w-1}x_{i}\cdot2^{ i} B2U(X)=i=0w1xi2i
转补码 我们学到的是正数的补码就是其本身, 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1).
B 2 T ( X ) = − x w − 1 ⋅ 2 w − 1 + ∑ i = 0 w − 2 x i ⋅ 2 i B2T(X) = -x_{w-1}\cdot2^{w-1}+\sum_{i=0}^{w-2}x_{i}\cdot2^{i} B2T(X)=xw12w1+i=0w2xi2i
理解了之后, 可以知道 U m i n = 0 , U M a x = 2 w − 1 , T M i n = − 2 w − 1 , T M a x = 2 w − 1 − 1 Umin = 0, UMax = 2^w - 1, TMin = -2^{w-1}, TMax = 2^{w-1} - 1 Umin=0,UMax=2w1,TMin=2w1,TMax=2w11. 其中补码的 w − 1 w-1 w1 可以理解成减去了最高位符号位.

此外, 从公式上也可以观察到, $ |TMin| = TMax + 1, UMax = 2 * TMax + 1$.

T T T 的-1, 以及 U M a x UMax UMax 111...1 111...1 111...1. 从公式上或者之前学到的都可以理解.

在做强制转换(Casting)的时候要注意这些特性, 比如 − 1 > 0 U -1 > 0U 1>0U , 这是因为无符号数的转换导致了-1变成了 U M a x UMax UMax. 混合计算的默认情况下有符号数会被隐式转换成无符号数.

所以要知道, − 1 > − 2 , − 1 U > − 2 -1 > -2, -1U > -2 1>2,1U>2 , 两者的底层逻辑是不一样的.

又比如 2147483647 U < − 2147483647 − 1 2147483647U < -2147483647-1 2147483647U<21474836471, 这时候前者是 T M a x = 01...1 TMax = 01...1 TMax=01...1, 后者是 T M i n = 100..0 TMin = 100..0 TMin=100..0, 都是无符号数的情况下, 两者差1.

#include <iostream>
#include <climits>
using namespace std;

int main() {
    unsigned int i = INT32_MAX; // 2147483647U
    int j = INT32_MIN; // -2147483647-1 = -2147483648
    unsigned k = j;
    cout << "i: " << i << endl;
    cout << "j: " << j << endl;
    cout << "k: " << k << endl;
    bool res = i < j;
    cout << "res: " << res << endl;
    return 0;
}

// i: 2147483647
// j: -2147483648
// k: 2147483648
// res: 1

T M i n TMin TMin − T M a x − 1 -TMax -1 TMax1 的写法是因为 T M i n = 100..0 , T M a x = 01...1 TMin = 100..0, TMax = 01...1 TMin=100..0,TMax=01...1, 在编程中,特别是处理整数范围时,我们有时需要定义一个最小值和最大值。例如,在处理有符号整数(signed integer)时,最大值(通常称为TMax)和最小值(通常称为TMin)是非常常见的定义。通常,这些值在表示整数的比特位数上有特定的关系。

以32位有符号整数为例:

  • 最大值(TMax):因为最高位是符号位,正数的最大值是 2 31 − 1 2^{31} - 1 2311,即2147483647。
  • 最小值(TMin):最小值是负数,且比最大值多一个单位,因为0在正数的范围内。最小值是 − 2 31 -2^{31} 231,即-2147483648。

在这种情况下,最小值TMin可以用公式表示:$ TMin = -TMax - 1 $

这背后的原因是:

  1. 数值对称性:有符号整数范围是对称的,负数部分比正数部分多一个单位(因为包括0)。因此,负数的范围从 − 2 31 -2^{31} 231到-1,而正数的范围从0到 2 31 − 1 2^{31} - 1 2311
  2. 比特表示:有符号整数在计算机内部是用补码表示的。补码表示的特点使得最小负数比最大正数多一个单位。

通过这种表示方法,可以简化整数范围的定义和操作,也有助于在编写代码时避免溢出错误。简单来说, T M i n = − T M a x − 1 TMin = -TMax - 1 TMin=TMax1这一写法是对有符号整数范围的一种精确定义,确保了范围的对称性和正确性。

转换 (Cast)

符号扩展 (Sing Expand)

比如8位数字扩展到16位数字, 如何去在不改变实际值的情况下扩展?

正数的情况, 符号位是0, 往前扩展0.

负数的情况, 符号位是1, 往前扩展1. 比如 1110 1110 1110, 此时可以理解成 − 8 + 4 + 2 = − 2 -8+4+2=-2 8+4+2=2, 用取反+1也可以算. 扩展后变成 11110 11110 11110, 此时是 − 16 + 8 + 4 + 2 = − 2 -16+8+4+2=-2 16+8+4+2=2 , 可以看到 − 16 + 8 = − 8 -16+8=-8 16+8=8, 新权重和旧权重加起来, 总和依然不变. 这也是二进制的特性.

总结一下就是无符号数扩展0, 有符号数扩展符号位.

截断 (Truncate)

基本是直接截断位数. 截断会不可避免地导致数据丢失, 所以要尽量避免.

无符号位的情况, 比如 11011 = 27 , 1011 = 11 11011 = 27, 1011 = 11 11011=27,1011=11, 也可以说是 m o d 16 mod16 mod16.

有符号位的情况, 比如 11011 = − 5 , 1011 = − 5 11011 = -5, 1011 = -5 11011=5,1011=5. 这是Sign Expand带来的错觉, 需要理解成 m o d 16 mod 16 mod16.

或者说 10011 = − 13 , 0011 = 3 10011 = -13, 0011 = 3 10011=13,0011=3, 这是 $ (16+2+1)mod16 = 3$.

在计算机中,有符号整数的表示方式通常使用补码(Two’s Complement)。补码的一个重要特性是,可以用统一的方式表示正数和负数,并且可以直接使用二进制加法器来处理加减运算。

对于一个二进制数,如果我们将其截断到更少的位数,会影响它的表示和值。以下是详细解释:

补码表示法

首先,理解补码的基本概念。对于一个n位的二进制数:

  • 正数的补码表示与其原码(普通二进制表示)相同。
  • 负数的补码表示是将该数的绝对值按位取反,然后加1。

示例分析

假设我们有一个5位的二进制数 10011,以及一个4位的二进制数 0011。我们来逐步分析它们的补码表示和截断后的影响。

1. 原数 10011 (5位)
  • 这个数是一个5位的二进制数,最高位是1,表示这是一个负数。
  • 计算其补码:
    • 取反:10011 -> 01100
    • 加1: 01100 + 1 = 01101
    • 所以,10011 的绝对值是 01101 (即13),原数是 -13
2. 截断到4位:0011
  • 截断后只保留低4位:0011
  • 0011 是一个4位的二进制数,最高位是0,表示这是一个正数。
  • 直接计算值:0011 的值是 3

截断导致的问题

截断的过程中,最左边的一位被舍弃,这改变了数值的符号和大小。在补码表示法中,最高位(符号位)的变化会直接影响数值的正负,因此截断后的结果可能与原来的数值有很大的差异。

总结起来,10011 是一个5位的负数,截断为 0011 后变成了4位的正数,这种截断操作破坏了原有的补码结构,导致负数 -13 变成了正数 3

图示

更直观的解释可以通过图示:

原数: 10011 (5位,补码表示 -13)
截断: 0011  (4位,补码表示 3)

因此,截断操作改变了符号位和数值位,导致 -13 变成 3

构,导致负数 -13 变成了正数 3

图示

更直观的解释可以通过图示:

原数: 10011 (5位,补码表示 -13)
截断: 0011  (4位,补码表示 3)

因此,截断操作改变了符号位和数值位,导致 -13 变成 3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值