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

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

#### 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)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://zhenghe.gitbook.io/open-courses/stanford-cs107/di-er-ke-yuan-shi-shu-ju-lei-xing-ji-xiang-hu-zhuan-hua.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
