> For the complete documentation index, see [llms.txt](https://zhenghe.gitbook.io/open-courses/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://zhenghe.gitbook.io/open-courses/stanford-cs107/di-er-ke-yuan-shi-shu-ju-lei-xing-ji-xiang-hu-zhuan-hua.md).

# 原始数据类型及相互转化

本文为本系列笔记的第一篇，因为第一课为整体课程介绍，无实质内容，但为了与课程保持一致，本系列笔记从第二课开始

#### c/c++ 原始数据类型 <a href="#cc-yuan-shi-shu-ju-lei-xing" id="cc-yuan-shi-shu-ju-lei-xing"></a>

| bool | char | short | int  | long | float | double |
| ---- | ---- | ----- | ---- | ---- | ----- | ------ |
|      | 1 字节 | 2 字节  | 4 字节 | 4 字节 | 4 字节  | 8 字节   |

**举例**

```c
char ch = 'A'; // 65         -> 01000001
short s = 519; // 519        -> 00000010 | 00000111
```

**负数的表示形式**

以 short 为例，1个 short 类型有 2 字节，共包含 65536 种状态。如果都表示自然数，那么可以表示 0\~65535 之间的所有自然数。但我们希望计算机能区别正负数，此时最合乎逻辑的做法就是：

> 拿出这里面一半的状态来表示负数，即 -32767 \~ 32768

因此，取最高位比特 (most significant bit) 表示符号，最高位是 1 表示负数，最高位是 0 表示正数：

```
 7: 00000000 00000111
-7: 10000000 00000111
```

计算机中最基本的运算除了位运算就是加减法，如果用当前的表示法计算 -7 + 7：

```
  00000000 00000111
+ 10000000 00000111
-------------------
= 10000000 00001000
```

那么，我们得到的数字是 -8，我们希望计算计在运行时能更优雅地处理加减法，使得 -7 + 7 得到的数字是 0

```
  00000000 00000111      00000000 00000111      00000000 00000111
+ xxxxxxxx xxxxxxxx => + 11111111 11111000 => + 11111111 11111001
-------------------    -------------------    -------------------
= 00000000 00000000    = 11111111 11111111    = 00000000 00000000
```

将一个二进制正数转化为对应的计算机二进制负数：(同理)

```
11111111 11111001 =翻转所有比特位=> 00000000 00000110 =加1=> 00000000 00000111
```

这种表示法的学名是 **2's complement**

**浮点数的表示形式**

以 float 类型 (4 bytes) 来举例：为了表示浮点数，我们势必需要拿出一些位来表示小数部分 (fractions)，如：

```
// 1 位小数位
00000000 00000000 00000000 0000000[1] 表示 0.5
// 2 为小数位
00000000 00000000 00000000 000000[11] 表示 0.75
```

但通过观察可以知道：4 个字节一共只能表示 4294967296 种数字，而实数空间有无穷多个数，因此我们能准确表示的数非常有限。为了满足日常需求，我们希望更好地利用这 4 个字节来表示的数字>范围：

1. 数量级范围较广
2. 对于量级靠近 0 的数能尽量准确表示

因此，计算机科学家将 4 个字节拆成了 符号位，指数位和小数位来表示浮点数：

```
float:
  sign     exponent                      fractions
| +/-  | <-------8-------> | <---------------23-------------------> |

(-1)^(sign) * 1.xxxx... * 2^(exp - 127) 其中 1.xxxx... 来自于 fractions

double:
  sign     exponent                      fractions
| +/- | <--------11-------> | <----------------52-------------------> |
```

举例如下：

```
float: -7.0 = -1.75 * 2^2 因此，符号位是 1,指数位是2，小数位是 0.75
=> |1|10000001|11000000000000000000000|
```

#### 小端 (Little Endian) 和 大端 (Big Endian) <a href="#xiao-duan-littleendian-he-da-duan-bigendian" id="xiao-duan-littleendian-he-da-duan-bigendian"></a>

Endianness 指的是在多个字节表示的数值类型 (short, long, float, double) 中，字节的排列顺序。最高位所在的字节称为大端，最低位所在的字节称为小端。那么可以按如下方式记住小端存储和大端存储：

* Little Endian -> Little End -> 从 Little End 开始存储
* Big Endian -> Big End -> 从 Big End 开始存储

举例如下：

```c
short s = 7;
// Big Endian:     => 00000000 00000111
// Little Endian:  => 00000111 00000000
float f = -7.0;
// Big Endian:     => 11000000 11100000 00000000 00000000
// Little Endian:  => 00000000 00000000 11100000 11000000
```

#### 原始数据类型之间的转化 <a href="#yuan-shi-shu-ju-lei-xing-zhi-jian-de-zhuan-hua" id="yuan-shi-shu-ju-lei-xing-zhi-jian-de-zhuan-hua"></a>

**char 转 short**

```c
char ch = 'A';
short s = ch;

// Big Endian:
// ch: =>          01000000
// s:  => 00000000 01000001

// Little Endian:
// ch: => 01000001
// s:  => 01000001 00000000
```

**short 转 int**

```c
short s = 1033;
int i = s;

// Big Endian:
// s:  =>                   00000100 00001001
// i:  => 00000000 00000000 00000100 00001001

// Little Endian:
// s:  => 00001001 00000100
// i:  => 00001001 00000100 00000000 00000000

short s = -1;
int i = s;

// Big Endian: (符号位延续)
// s:  =>                   11111111 11111111
// i:  => 11111111 11111111 11111111 11111111

// Little Endian:
// s:  => 11111111 11111111
// i:  => 11111111 11111111 11111111 11111111
```

**int 转 short**

```c
int i = 10502151;
short s = i;

// Big Endian:
// s:  =>                   01000000 00000111
// i:  => 00000000 10100000 01000000 00000111

// Little Endian:
// s:  => 00000111 01000000
// i:  => 00000111 01000000 10100000 00000000

int i = -65536;
short s = i;

// Big Endian:
// s:  =>                   00000000 00000000
// i:  => 11111111 11111111 00000000 00000000

// Little Endian:
// s:  => 00000000 00000000
// i:  => 00000000 00000000 11111111 11111111
```

**int 转 float**

```c
int i = 5;
float f = i;

// 神奇地转化，非截断，而是保持原数值大小，这也是方便平时计算需求
// f:  => 01000001 00100000 00000000 00000000
// i:  => 00000000 00000000 00000000 00000101
```

#### 参考资料 <a href="#can-kao-zi-liao" id="can-kao-zi-liao"></a>

* [Stanford CS107: lecture 2](https://www.youtube.com/watch?v=jTSvthW34GU\&list=PL08D9FA018A965057)
* [Github: ZhengHe-MD - lecture codes](https://github.com/ZhengHe-MD/cs107-lecture-codes)
