浮点数,数符、阶码、阶符、尾数分别是什么

浮点数的基本思想

浮点数不是直接把所有二进制位都拿来表示一个定点小数,而是把一个数拆成:

浮点数真值 = 数符 × 尾数 × 基数^阶码

更常见地写成:

N = (-1)^S × M × R^E

其中:

S 是数符,用来表示整个数的正负。

M 是尾数,也叫有效数、有效数字部分,主要决定这个数“有多精细”。

R 是基数,二进制计算机中通常是 2,所以一般写成 2^E

E 是阶码,也就是指数,主要决定小数点移动到哪里,因此主要决定表示范围。

例如二进制数:

101.101₂

可以写成:

1.01101₂ × 2²

这里 1.01101₂ 就是尾数部分,2 是基数,2 上面的指数 2 是阶码。


数符、阶码、阶符、尾数分别是什么

在计组教材里,浮点数格式通常被拆成几段:

数符 | 阶码 | 尾数

或者更细一点写成:

数符 | 阶符 | 阶码数值部分 | 尾数

这里要注意,有些教材中的“阶码”会包含“阶符”,有些教材会把“阶符”和“阶码数值”分开讲。

数符表示整个浮点数的正负。例如:

数符 = 0,表示正数
数符 = 1,表示负数

阶码表示指数。它的作用不是表示数本身有多少有效数字,而是表示小数点移动的位置。阶码越大,数值范围通常越大;阶码越小,数值越接近 0。

如果教材采用“阶符 + 阶码数值”的表示方式,那么阶符表示指数的正负。例如:

阶符 = 0,表示阶码为正
阶符 = 1,表示阶码为负

但在 IEEE 754 标准浮点数中,一般不用单独的阶符,而是用移码,也叫偏置码,来表示阶码。例如单精度浮点数的阶码字段有 8 位,偏置值是 127,真实阶码为:

真实阶码 = 阶码字段值 - 127

尾数表示有效数字。例如:

1.01101₂ × 2²

里面的 1.01101₂ 是尾数, 负责把小数点整体移动。

基数就是指数运算的底数。二进制浮点数的基数通常是 2,所以浮点数一般形如:

±M × 2^E

在考试题里,基数通常是隐含的,不一定单独占机器位。比如 IEEE 754 的单精度浮点数,机器字中存数符、阶码、尾数字段,但基数默认就是 2。


尾数和阶码的分工:一个管精度,一个管范围

浮点数可以这样理解:

尾数决定有效数字
阶码决定小数点位置

比如十进制中:

3.14159 × 10³ = 3141.59
3.14159 × 10⁻³ = 0.00314159

这两个数的尾数都是 3.14159,所以它们保留的有效数字数量一样;但是阶码不同,所以小数点位置不同,数值范围完全不同。

二进制浮点数同理:

1.10101₂ × 2⁵
1.10101₂ × 2⁻⁵

二者尾数相同,精度相同,但数量级不同。

所以考研题里经常说:

阶码位数影响表示范围
尾数位数影响表示精度

这句话非常重要。


精度不是简单指“小数点后有几位”

这里最容易误解。

浮点数的精度不是指“小数点后面能有几位”,而是指“尾数能表示多少位有效数字”。

对于定点小数来说,确实可以说小数点后有几位,因为小数点位置固定。

但浮点数的小数点位置是浮动的,由阶码控制。尾数中保存的是有效数字,不是固定意义上的“小数点后若干位”。

例如十进制科学计数法:

1.2345 × 10² = 123.45
1.2345 × 10⁻² = 0.012345

如果尾数只能保存 5 位有效数字,那么它们都只有 5 位有效数字精度。不能说第一个数“小数点后只有 2 位所以精度低”,也不能说第二个数“小数点后有 6 位所以精度高”。真正决定精度的是尾数里的有效数字位数。

二进制浮点数也是一样。如果尾数有效位数是 24 位,那么它大约能提供 24 位二进制有效精度,换算成十进制大约是 7 位有效数字。

所以更准确的说法是:

浮点数精度 = 尾数能保存的有效位数

而不是:

浮点数精度 = 小数点后有几位

不过在 IEEE 754 中,尾数字段看起来确实是“小数点后的部分”,例如规格化数写成:

1.f × 2^E

其中 f 是存储的 fraction 字段,也就是小数点后的尾数字段。但有效精度不只包括 f,还包括前面那个默认的隐藏 1

例如单精度浮点数:

尾数字段 = 23 位
隐藏位 = 1 位
实际有效精度 = 24 位

这也是“隐藏 1”带来的好处。


什么是规格化浮点数

规格化的目的,是让同一个数只有一种标准表示形式,并且让尾数尽量保留有效位。

比如十进制中:

123.45 = 12.345 × 10¹
123.45 = 1.2345 × 10²
123.45 = 0.12345 × 10³

同一个数可以有很多种表示。如果不规定统一格式,机器表示会混乱,也会浪费有效位。

所以科学计数法通常规定:

1 ≤ 尾数 < 10

于是 123.45 的规格化表示就是:

1.2345 × 10²

二进制中,规格化思想类似。非零二进制数规格化后,最高有效位一定是 1

常见有两种写法。

第一种是 IEEE 754 常用写法:

1.xxxxx₂ × 2^E

尾数范围是:

1 ≤ M < 2

第二种是一些计组教材讲机器浮点数时常用的纯小数尾数写法:

0.1xxxxx₂ × 2^E

尾数范围是:

1/2 ≤ |M| < 1

这两种写法本质等价,只是小数点放的位置不同,阶码会相应差 1。

例如:

1.101₂ × 2³

也可以写成:

0.1101₂ × 2⁴

它们表示的是同一个数。


“最小规格化数”是什么意思

最小规格化数,通常指“绝对值最小的正规格化浮点数”。

要让一个正规格化浮点数尽可能小,需要同时满足两个条件:

阶码取最小值
尾数取规格化条件下的最小值

如果采用 IEEE 754 的形式:

1.xxxxx₂ × 2^E

那么规格化尾数最小是:

1.00000...₂

所以最小正规格化数是:

1.00000...₂ × 2^Emin

其中 Emin 是允许的最小真实阶码。

如果采用教材中常见的纯小数形式:

0.1xxxxx₂ × 2^E

那么规格化尾数最小是:

0.10000...₂

所以最小正规格化数是:

0.10000...₂ × 2^Emin

这里不要死记某一种形式,要先看题目采用的是哪种尾数约定。

考研里常见判断方式是:

如果题目说规格化二进制尾数形如 1.xxx,那最小尾数是 1.000...

如果题目说规格化二进制尾数是纯小数形式,形如 0.1xxx,那最小尾数是 0.100...


为什么规格化二进制浮点数可以隐藏最高位的 1

这是 IEEE 754 里的一个关键设计。

对于非零的规格化二进制数,它的尾数一定可以写成:

1.xxxxx₂

因为二进制中非零数的最高有效位一定是 1。规格化之后,最高有效位被移动到小数点左边,所以一定是:

1.

不可能是:

0.xxxx

也不可能是:

2.xxxx

因为二进制每一位只能是 0 或 1,规格化以后尾数范围固定在:

1 ≤ M < 2

所以最高位这个 1 对所有非零规格化数来说都是必然存在的。既然一定是 1,机器就没有必要把它存下来。

于是 IEEE 754 只存小数点后的部分:

实际尾数:1.f
机器只存:f

这个没有存储、但默认存在的最高位 1,就叫隐藏位,也叫隐含位。

例如某个规格化尾数是:

1.101011₂

机器中尾数字段只存:

101011

读取时,硬件自动在前面补上隐藏的 1

1.101011₂

隐藏 1 为什么能“多表示一位有效值”

假设尾数字段有 23 位。

如果不隐藏最高位,那么 23 位字段要完整保存:

1.xxxxxxxxxxxxxxxxxxxxxx

其中最高位的 1 也要占一个存储位。

但由于规格化二进制数最高位必定是 1,所以可以不存它,把这 1 位空间省下来,用来保存后面更多的有效尾数位。

于是:

存储尾数字段:23 位
实际有效尾数:1 位隐藏位 + 23 位显式尾数
实际精度:24 位

这就是“为了使尾数多表示一位有效值,所以将规格化二进制浮点数最高位的 1 隐藏”的含义。

它不是说数值凭空变多,而是说同样的存储位数下,能多保留一位有效二进制位,从而提高精度。

以 IEEE 754 单精度为例:

数符:1 位
阶码:8 位
尾数字段:23 位

但规格化数的实际尾数精度是:

24 位 = 隐藏的 1 位 + 显式存储的 23 位

这也是为什么经常说 IEEE 754 单精度有效精度约为 24 位二进制位,大约等价于 7 位十进制有效数字。


隐藏 1 只适用于规格化数

这一点也很容易考。

隐藏 1 的前提是:

该数是非零规格化二进制浮点数

因为只有规格化数才能保证最高有效位一定是 1

对于非规格化数,也叫 denormal 或 subnormal number,尾数通常不是:

1.f

而是:

0.f

这时最高位不能默认补 1,否则数值就错了。

所以 IEEE 754 中:

规格化数:尾数为 1.f,有隐藏 1
非规格化数:尾数为 0.f,没有隐藏 1

零、无穷大、NaN 这些特殊值也不使用普通规格化数的隐藏 1 规则。


408 做题时的快速判断

做浮点数表示类题目时,先不要急着算,先判断题目使用的是哪套格式。

如果题目是一般教材格式,可能会出现:

数符 | 阶符 | 阶码 | 尾数

此时要看尾数是原码、补码还是反码,规格化条件也要按教材规定判断。

如果题目明确是 IEEE 754,则默认:

单精度:1 位数符 + 8 位阶码 + 23 位尾数
双精度:1 位数符 + 11 位阶码 + 52 位尾数

并且规格化数的真实形式是:

(-1)^S × 1.f × 2^(E - bias)

其中单精度 bias 是 127,双精度 bias 是 1023。

这类题的核心规律可以记成:

数符决定正负
阶码决定范围
尾数决定精度
基数通常默认是 2
规格化保证最高有效位固定
隐藏 1 用固定的最高位换来多 1 位有效精度

最重要的是不要把“尾数字段位数”直接等同于“实际精度”。在 IEEE 754 规格化数中:

实际有效精度 = 尾数字段位数 + 1 个隐藏位

你提到二进制中非零数的最高有效位一定是一,那么如果此时这个数为负数的时候,是这个数的符号为一,那么此时最高有效位应该是零啊,所以说关于这个IEEE 754还是不太理解

你这里的困惑非常关键:IEEE 754 里的“最高有效位一定是 1”,说的是“尾数绝对值部分”的最高有效位,不是整个机器码的最高位,也不是符号位。

换句话说,IEEE 754 不是用补码方式直接表示整个浮点数,而是把一个数拆成三块:

符号位 S | 阶码 E | 尾数字段 fraction

它的真值形式是:

(-1)^S × 1.f × 2^(E - bias)

这里的 S 单独决定正负;1.f 表示这个数的有效数字部分,也就是尾数的绝对值部分。

所以对于负数来说,不是把整个数变成补码,也不是让尾数最高位变成 0,而是:

符号位 S = 1
尾数仍然按正的有效数来存
阶码仍然表示指数

例如十进制数 -6.5

先不看正负,只看它的绝对值:

6.5₁₀ = 110.1₂

规格化:

110.1₂ = 1.101₂ × 2²

所以:

-6.5 = (-1)^1 × 1.101₂ × 2²

在 IEEE 754 中:

符号位 S = 1
真实阶码 = 2
尾数有效形式 = 1.101₂
机器中只存小数点后的 101...

也就是说,负号由最前面的符号位表示,而尾数部分仍然是 1.101₂,不是补码。


“最高有效位一定是 1”到底指哪一位

前面说的“非零二进制数的最高有效位一定是 1”,严格来说是指:

非零数的绝对值写成二进制后,最高有效数字一定是 1

例如:

+6.5 的绝对值:110.1₂,最高有效位是 1
-6.5 的绝对值:110.1₂,最高有效位也是 1

负数的符号不参与这里的“最高有效位”判断。符号只是告诉机器:最后结果要乘以 -1

所以 IEEE 754 规格化后的尾数统一看成:

1.xxxxx₂

正数如此,负数也如此。

区别只在符号位:

+6.5 = (-1)^0 × 1.101₂ × 2²
-6.5 = (-1)^1 × 1.101₂ × 2²

尾数的有效数字部分完全一样,只有 S 不同。


IEEE 754 不是补码浮点数

你的疑惑很可能来自整数补码的思维。

在整数补码中,最高位通常既参与数值编码,也表示符号倾向。例如 8 位补码:

01111111 表示 +127
10000000 表示 -128
11111111 表示 -1

所以整数补码里,看到最高位是 1,通常说明这个整数是负数。

但是 IEEE 754 浮点数不是这样。

IEEE 754 浮点数更像“符号-数值”结构:

符号位:单独表示正负
阶码:表示指数
尾数:表示有效数字的绝对值部分

它不是把整个浮点数当成一个补码整数来解释。

所以不能这样想:

负数 → 整个机器码按补码表示 → 尾数也要按补码理解

这条思路是错的。

应该这样想:

负数 → 符号位 S = 1
数值大小 → 由阶码和尾数表示

举一个 IEEE 754 单精度的结构例子

IEEE 754 单精度格式是:

1 位符号位 | 8 位阶码 | 23 位尾数字段

如果表示 -6.5

-6.5 = (-1)^1 × 1.101₂ × 2²

符号位:

S = 1

真实阶码:

E = 2

单精度偏置值是 127,所以阶码字段为:

2 + 127 = 129 = 10000001₂

尾数是:

1.101₂

隐藏最高位 1,只存小数点后的部分:

10100000000000000000000

所以整体结构是:

1 | 10000001 | 10100000000000000000000

这里最左边的 1 是符号位,表示负数。

尾数中隐藏的那个 1 不在机器字中显式出现,它是机器解释尾数字段时自动补出来的:

机器存:10100000000000000000000
实际尾数:1.10100000000000000000000

所以这里有两个完全不同的“1”:

最左边的 1:符号位,表示负号
隐藏的 1:尾数最高有效位,表示规格化有效数的整数部分

它们不是同一类东西。


为什么负数的尾数最高位仍然是 1

因为 IEEE 754 的尾数表示的是“数值大小”的有效数字,而不是带符号的补码。

例如:

+0.75 = +0.11₂
-0.75 = -0.11₂

先把绝对值规格化:

0.75 = 0.11₂ = 1.1₂ × 2⁻¹

所以:

+0.75 = (-1)^0 × 1.1₂ × 2⁻¹
-0.75 = (-1)^1 × 1.1₂ × 2⁻¹

你会发现,正负两个数的尾数都是:

1.1₂

只是符号位不同。

这和整数补码完全不同。整数补码中,+x-x 的编码通常差别很大;而 IEEE 754 浮点数中,+x-x 的阶码、尾数字段通常相同,只是符号位不同。

例如:

+6.5:0 | 10000001 | 10100000000000000000000
-6.5:1 | 10000001 | 10100000000000000000000

除了符号位,后面完全一样。


“最高有效位应该是 0”这个想法错在哪里

这句话需要拆开看。

如果你说的是“负数的机器码最高位”,那在 IEEE 754 中,它确实是 1,因为最高位就是符号位。

负数的 IEEE 754 最高机器位 = 1

如果你说的是“尾数的最高有效位”,那对于规格化数,它一定是 1,不论正负。

规格化尾数最高有效位 = 1

如果你说的是“补码负数的数值部分”,那这已经不是 IEEE 754 的理解方式了。IEEE 754 浮点数不是整体补码,也不是尾数补码。

所以这三个概念不能混在一起:

机器字最高位:IEEE 754 中是符号位
尾数最高有效位:规格化后一定是 1
整数补码最高位:用于判断整数正负

在 IEEE 754 中,隐藏的是“尾数最高有效位的 1”,不是隐藏符号位,也不是隐藏整个机器码的最高位。


和一般计组教材浮点格式的区别

408 复习时还要注意:教材里讲浮点数时,有时候不是直接讲 IEEE 754,而是讲一般浮点数格式,例如:

数符 | 阶符 | 阶码 | 尾数

有些教材还会规定尾数用原码、补码或反码表示。这时候规格化判断会稍微复杂一些。

但 IEEE 754 的核心规则非常明确:

符号位单独表示正负;
阶码用移码表示;
尾数用原码思想表示有效数字;
规格化数默认尾数最高位为 1;
机器中不存这个最高位 1,所以叫隐藏位。

所以在 IEEE 754 中,不能用“补码负数”的思路去解释尾数。


适合做题时记住的结论

IEEE 754 浮点数可以直接记成:

浮点数真值 = (-1)^S × 1.f × 2^(阶码字段值 - 偏置值)

对于规格化数:

S = 0,表示正数
S = 1,表示负数

1.f 中的 1 是隐藏位
f 是机器中真正存储的尾数字段

因此:

+N 和 -N 的 IEEE 754 编码通常只有符号位不同

例如:

+6.5:0 | 10000001 | 10100000000000000000000
-6.5:1 | 10000001 | 10100000000000000000000

最关键的一句话是:

IEEE 754 中,负号由符号位单独表示;尾数表示绝对值的有效数字,所以规格化尾数最高有效位仍然是 1。

既然浮点数类型中,int和float double和long都是长度一样的,那么它们俩的区别是什么?以及浮点元素类型及转化中,谁在转化率迅速更高,应在进行比较的时候,应该是朝那里进行转化,我还是不太理解浮点数中的上意和下意,这种情况就是说这个0.1xx或者10.0xx到底是怎么弄出来的?

你这里其实把三个容易混在一起的点放到了一起:类型长度相同不代表表示方法相同;混合运算时不是看谁“快”,而是看类型转换等级;浮点数的上溢、下溢和规格化形式,都是由阶码范围和尾数规格决定的。

一、int、float、double、long 位数一样,为什么还不一样?

先纠正一个前提:intfloatdoublelong 不一定长度都一样。

在 408 或计组题目中,常见默认理解是:

int    通常 32 位
float  通常 32 位
double 通常 64 位
long   不同环境可能是 32 位或 64 位

在 C 语言标准里,并没有强制规定 int 一定 32 位、long 一定 64 位。考试题如果严格考,一般会给出机器字长或类型长度。没有给时,很多题默认按常见平台理解:intfloat 都是 32 位,double 是 64 位。

但是重点是:长度相同,不代表含义相同。

比如 intfloat 都可能是 32 位,但它们的 32 位分配方式完全不同。

int 通常按补码表示整数:

32 位 int:
1 位符号倾向 + 31 位数值权重
表示范围约为 -2³¹ 到 2³¹ - 1
整数范围内逐个连续、精确表示

float 按 IEEE 754 单精度浮点数表示:

32 位 float:
1 位符号位 + 8 位阶码 + 23 位尾数字段
实际有效精度约为 24 位二进制有效位
表示范围很大,但不是所有整数和小数都能精确表示

所以同样是 32 位:

int   范围较小,但范围内整数通常精确、连续
float 范围很大,但精度有限,很多数只能近似表示

这就是最核心区别。

例如:

int 可以精确表示 16777217
float 不能精确表示 16777217

因为单精度 float 的有效精度只有 24 位二进制有效位。超过 2²⁴ 附近以后,并不是每一个整数都能被 float 精确表示。

所以不能说:

32 位 int 和 32 位 float 都是 32 位,所以能力一样

应该说:

位数一样,但编码方式不同,表示范围、精度、用途都不同。

二、float 和 double 的区别是什么?

float 是单精度浮点数,double 是双精度浮点数。

常见 IEEE 754 格式如下:

类型总位数符号位阶码位尾数字段位实际有效精度
float32 位1 位8 位23 位24 位
double64 位1 位11 位52 位53 位

这里的“实际有效精度”比尾数字段多 1 位,是因为规格化浮点数有隐藏位 1

所以:

float  范围较小,精度较低
double 范围更大,精度更高

在做题中可以直接记:

float 约有 7 位十进制有效数字
double 约有 15~16 位十进制有效数字

注意,这里的“7 位”“15~16 位”说的是有效数字位数,不是小数点后几位。

例如:

1234567.0
0.000001234567

二者小数点位置不同,但如果都保留 7 位有效数字,精度意义是类似的。


三、long 和 float / double 的区别

long 是整数类型,不是浮点类型。

如果某个平台上:

long 是 64 位
double 也是 64 位

它们也完全不是一回事。

64 位 long 通常表示整数,范围大约是:

-2⁶³ 到 2⁶³ - 1

它在这个整数范围内是逐个精确表示的。

64 位 double 表示浮点数,格式大致是:

1 位符号位 + 11 位阶码 + 52 位尾数字段

double 的范围远远大于 64 位 long,可以表示到约 10³⁰⁸ 量级,但它不能精确表示所有 64 位整数。

double 能连续精确表示的整数大约到:

2⁵³

超过 2⁵³ 后,很多相邻整数已经无法区分。

所以可以这样理解:

long:整数精确,范围相对有限
double:范围极大,但精度有限

这也是为什么程序里不能随便把很大的整数转成 double 后再比较,因为可能丢精度。


四、混合运算和比较时,应该朝谁转换?

你说的“谁转化率更高”,这里更准确应理解为:类型转换等级谁更高,混合运算时向哪个类型转换。

在 C 语言和 408 常见题里,混合运算大致遵循这个方向:

低等级类型 → 高等级类型
整数 → 浮点数
float → double → long double

可以粗略记成:

long double > double > float > 整型

当表达式中同时有整数和浮点数时,通常把整数转成浮点数再运算或比较。

例如:

int a = 3;
float b = 2.5f;

a + b

计算时,a 会先转成 float,然后进行浮点运算:

3 → 3.0f
3.0f + 2.5f = 5.5f

如果是:

int a = 3;
double b = 2.5;

a + b

a 转成 double

3 → 3.0
3.0 + 2.5 = 5.5

比较运算也是类似的。

例如:

int a = 3;
double b = 3.0;

a == b

比较前,a 会转换成 double

3 → 3.0
3.0 == 3.0

结果为真。

但是如果整数很大,就可能出问题。

例如:

int a = 16777217;
float b = 16777216.0f;

如果比较:

a == b

在比较时,a 可能被转换成 float。而 16777217 超过了 float 能连续精确表示整数的范围,转成 float 后可能变成:

16777217 → 16777216.0f

于是比较可能得到看起来反直觉的结果。

这类题本质考的是:

类型转换可能导致精度丢失

所以做题时不要只记“向高类型转换”,还要补一句:

向浮点类型转换后,范围可能变大,但整数精度可能丢失。

五、为什么比较时通常朝浮点数转?

因为浮点数能表示小数,整数类型不能直接表示小数。

例如:

int a = 3;
float b = 3.5f;

如果把 b 转成 int,就会丢掉小数部分:

3.5f → 3

这显然会严重改变数值。

所以混合运算一般把整数转成浮点数:

3 → 3.0f

这样至少可以保留小数运算的语义。

但这并不代表浮点数一定“更精确”。这句话很重要。

浮点数表示范围更大,但不一定比整数更精确。

比如 32 位 int 可以精确表示:

16777217

但 32 位 float 不能精确表示这个整数。

所以在考题里要分清两个概念:

表示范围:能表示多大或多小的数
表示精度:能把数表示得多细、多准确

浮点数范围大,不等于所有数都精确。


六、什么是浮点数的上溢和下溢?

你说的“上意和下意”,应该是“上溢”和“下溢”。

浮点数真值一般写成:

N = ±M × R^E

其中:

M 是尾数,决定精度
E 是阶码,决定范围
R 是基数,二进制中通常为 2

阶码有最大值和最小值。因为阶码字段位数有限,所以指数不能无限大,也不能无限小。

1. 上溢

当一个数太大,超过浮点数能表示的最大范围,就叫上溢。

也就是:

真实结果的绝对值 > 最大可表示浮点数

例如一个系统最多只能表示到:

1.111... × 2^Emax

如果运算结果需要:

1.xxx × 2^(Emax + 1)

那阶码已经不够用了,就是上溢。

在 IEEE 754 中,上溢通常会得到:

+∞ 或 -∞

在一般计组题中,也可能说“发生溢出,机器无法表示”。

408 里可以记:

上溢:阶码太大,数值绝对值太大。

2. 下溢

当一个非零数太接近 0,小到规格化浮点数无法表示,就叫下溢。

也就是:

0 < |真实结果| < 最小可表示正规格化数

例如最小正规格化数是:

1.000... × 2^Emin

如果结果是:

1.xxx × 2^(Emin - 1)

按规格化要求已经无法用更小阶码表示,就发生下溢。

IEEE 754 为了缓解这个问题,引入了非规格化数,也叫非正规数、subnormal number,使一部分更接近 0 的数还能被表示。但在很多 408 基础题里,若不讨论非规格化数,可以直接理解为:

下溢:阶码太小,非零数太接近 0,无法规格化表示。

七、0.1xxx1.xxx 到底是怎么弄出来的?

这个问题本质是:规格化浮点数的小数点应该放在哪里。

不同教材有两种常见写法。

写法一:IEEE 754 常用的 1.xxx × 2^E

IEEE 754 规格化二进制浮点数采用:

1.xxx × 2^E

意思是把二进制数移动小数点,使最高有效位放在小数点左边。

例如:

10110.01₂

把小数点向左移动 4 位:

10110.01₂ = 1.011001₂ × 2⁴

为什么是 2⁴

因为小数点向左移动了 4 位,数值变小了 2⁴ 倍。为了保持原值不变,需要乘回 2⁴

再看一个小数:

0.001101₂

最高有效位是从左往右第一个 1。把它调整成 1.xxx

0.001101₂ = 1.101₂ × 2⁻³

因为小数点向右移动了 3 位,尾数变大了 倍。为了保持原值不变,要乘以 2⁻³

所以规律是:

二进制数规格化成 1.xxx × 2^E
小数点左移几位,E 就加几
小数点右移几位,E 就减几

例如:

1101.01₂ = 1.10101₂ × 2³
0.01011₂ = 1.011₂ × 2⁻²

这就是 IEEE 754 中 1.xxx 的来源。


写法二:某些计组教材常用的 0.1xxx × 2^E

有些教材讲一般浮点格式时,喜欢把尾数写成纯小数形式:

0.1xxx × 2^E

这种写法要求尾数满足:

1/2 ≤ |M| < 1

也就是说,二进制尾数的小数点后第一位必须是 1

例如:

10110.01₂

也可以写成:

0.1011001₂ × 2⁵

为什么是 2⁵

因为:

0.1011001₂ × 2⁵
= 10110.01₂

这里小数点从 0.1011001 移到 10110.01,相当于向右移动 5 位,所以乘 2⁵

同一个数还可以用 IEEE 754 风格写成:

1.011001₂ × 2⁴

两种写法表示的是同一个数:

1.011001₂ × 2⁴ = 0.1011001₂ × 2⁵

差别只是尾数形式不同,阶码相差 1。

可以总结成:

1.xxx × 2^E       是 IEEE 754 常用规格化形式
0.1xxx × 2^(E+1)  是某些教材的纯小数尾数形式

八、10.0xx 是不是规格化?

如果按 IEEE 754 的规格化规则:

1.xxx × 2^E

那么 10.0xx × 2^E 不是规格化形式。

因为尾数 10.0xx₂ 的大小至少是 2,不满足:

1 ≤ M < 2

应该继续移动小数点,把它变成:

1.00xx × 2^(E+1)

例如:

10.01₂ × 2³

不是 IEEE 754 的规格化形式。应该改为:

1.001₂ × 2⁴

如果按教材中的纯小数尾数规则:

0.1xxx × 2^E

那么 10.0xx 更不是规格化尾数,因为尾数根本不是纯小数。

所以做题判断规格化时,先看题目规定:

如果规定尾数形式是 1.xxx,则规格化尾数必须满足 1 ≤ M < 2。
如果规定尾数形式是 0.1xxx,则规格化尾数必须满足 1/2 ≤ |M| < 1。

10.xxx 一般都不是规格化尾数,还要继续调整。


九、把 0.1xxx1.xxx 对照理解

假设有一个二进制数:

110.101₂

用 IEEE 754 风格规格化:

110.101₂ = 1.10101₂ × 2²

用纯小数尾数风格规格化:

110.101₂ = 0.110101₂ × 2³

两个都对,关键看题目要求。

再比如:

0.001011₂

IEEE 754 风格:

0.001011₂ = 1.011₂ × 2⁻³

纯小数尾数风格:

0.001011₂ = 0.1011₂ × 2⁻²

可以看到,两种形式的尾数小数点位置差 1 位,所以阶码也差 1。

这正是很多题容易混乱的地方:不是算错了,而是采用的规格化约定不同。


十、做题时最稳的判断流程

遇到浮点数类型与转换题,先判断数据类型:

int / long:整数类型,通常精确表示整数
float / double:浮点类型,范围大但精度有限

遇到混合运算或比较题,先判断转换方向:

有 double,通常转 double
有 float 且无 double,通常转 float
整数和浮点混合,通常整数转浮点

然后立刻追问自己一句:

转换后会不会丢精度?

尤其是大整数转 floatdouble 时,容易丢精度。

遇到浮点数规格化题,先看题目采用哪种尾数形式:

IEEE 754:1.xxx × 2^E
一般教材纯小数尾数:0.1xxx × 2^E

然后移动小数点:

小数点左移,阶码增加
小数点右移,阶码减少

遇到上溢、下溢题,抓住阶码:

上溢:阶码太大,数太大
下溢:阶码太小,数太接近 0

最后把这几句话记住,基本能覆盖 408 里大多数浮点数基础题:

位数相同,不代表类型相同。
整数类型重在精确表示整数。
浮点类型重在扩大表示范围,但精度有限。
阶码决定范围,尾数决定精度。
混合运算通常向更高等级类型转换,整数遇到浮点通常转浮点。
规格化的目的,是让尾数最高有效位固定,从而减少冗余、提高有效精度。
IEEE 754 规格化尾数是 1.xxx,一般教材也可能写成 0.1xxx。
上溢是太大,下溢是太小且接近 0。

第13题答案:A。
第14题答案:B,即 3F8C CCCD

第13题:浮点加法中的“对阶”怎么做

题目给出:

x = -126y = 15.75,二者都是 IEEE 754 单精度 float。

IEEE 754 单精度格式为:

符号位 S:1 位
阶码 E:8 位,偏置值为 127
尾数 M:23 位,规格化数默认隐藏最高位 1

先分别写成二进制规格化形式。

x = -126

126 = 1111110₂ = 1.111110₂ × 2⁶

所以:

x 的符号位 = 1
x 的真实阶码 = 6
x 的移码阶码 = 6 + 127 = 133 = 10000101₂

y = 15.75

15.75 = 1111.11₂ = 1.111110₂ × 2³

所以:

y 的符号位 = 0
y 的真实阶码 = 3
y 的移码阶码 = 3 + 127 = 130 = 10000010₂

浮点加法时,第一步是对阶。对阶原则是:

小阶向大阶对齐,阶码小的那个数尾数右移。

这里:

x 的真实阶码 = 6
y 的真实阶码 = 3

因此应该保持 x 不变,让 yx 对齐。

阶差为:

6 - 3 = 3

所以 y 的尾数右移 3 位,阶码变成和 x 一样的 10000101

y 原来的有效尾数是:

1.111110... × 2³

对齐到 2⁶ 后变成:

0.001111110... × 2⁶

因此对阶后:

x 不变
y 的符号位仍为 0
y 的阶码变为 10000101
y 的尾数右移为 0.001111110...0

对应选项 A:

x 不变,y 为 010000101, 0.00111110...0

这里前面的 010000101 可以理解为:

0 | 10000101
符号位 | 阶码

所以第13题选 A。

这题容易错在把“数值正负”与“阶码大小”混在一起。x 虽然是负数,但对阶比较的是指数部分,也就是数量级,不是比较代数大小。因为 |-126| 的数量级比 15.75 大,所以应让 yx 对齐。


第14题:1 + 0.1 的 IEEE 754 单精度表示

题目给出:

x = 1
y = 0.1
0.1 的二进制表示为 0.0001100110011...
重复因子为 0011
采用 IEEE 754 单精度格式和就近舍入方式

要求 x + y 的结果用十六进制机器数表示。

数学上:

x + y = 1 + 0.1 = 1.1

但是 0.1 不能用二进制有限位精确表示,所以结果要按 IEEE 754 单精度进行舍入。

1.1 的二进制形式为:

1.1₁₀ = 1.0001100110011001100110011...₂ × 2⁰

所以:

符号位 S = 0
真实阶码 = 0
移码阶码 E = 0 + 127 = 127 = 01111111₂

接下来取 23 位尾数字段。

小数点后的二进制为:

0001100110011001100110011...

前 23 位是:

00011001100110011001100

第 24 位是 1,后面还有非零位,所以按“就近舍入”要向上进 1。

因此尾数字段变成:

00011001100110011001101

于是完整的 32 位 IEEE 754 单精度机器数为:

0 01111111 00011001100110011001101

按 4 位一组转成十六进制:

0011 1111 1000 1100 1100 1100 1100 1101

即:

3F8C CCCD

所以第14题选 B。

选项 C 3F8C CCCC 很有迷惑性,它相当于只截断、不进位;但题目明确说“就近舍入”,所以最后一位要从 C 进到 D。选项 A 3F80 00001.0,只有当 0.1 在对阶后完全被丢掉时才可能出现,但这里阶差不大,不能丢掉。

15~17题答案依次为:

15:A
16:C
17:B


第15题:IEEE 754 中非规格化浮点数怎么表示?

本题选 A:阶码为 0,尾数为任意非 0 的二进制数

在 IEEE 754 单精度浮点数中,32 位格式为:

符号位 S:1 位
阶码 E:8 位
尾数字段 M:23 位

它的特殊编码规则要记住:

阶码字段尾数字段表示含义
E = 0,M = 0全 0±0
E = 0,M ≠ 0非 0非规格化数
0 < E < 255任意规格化数
E = 255,M = 0全 0±∞
E = 255,M ≠ 0非 0NaN

所以非规格化数的判断条件就是:

阶码全 0,尾数字段非 0

注意这里的“尾数为任意非 0 的二进制数”,更严格地说是“尾数字段非 0”。非规格化数没有隐藏的前导 1,它的有效数形式是:

0.M × 2^(1-bias)

而规格化数才是:

1.M × 2^(E-bias)

因此第15题选 A


第16题:对阶时,隐藏的前导 1 怎么处理?

本题选 C:对阶移位前,需先将隐含的“1”恢复到尾数高位,再整体右移

IEEE 754 规格化数的尾数并不是只看 23 位尾数字段。对于规格化数,真实参与运算的有效数是:

1.M

其中最高位的 1 不存储在机器数中,而是隐含存在的,这就是所谓“隐藏位”或“隐含的前导 1”。

浮点加减法的一般流程是:

0. 操作数拆分:符号位、阶码、尾数
1. 对阶:小阶向大阶看齐
2. 尾数加减
3. 规格化
4. 舍入
5. 判断溢出/下溢

在对阶阶段,如果两个数阶码不同,就要把阶码较小的那个数的尾数右移。关键是:右移的不是单纯的 23 位尾数字段,而是真实有效数。

例如规格化数的尾数字段为:

M = 010100...

真实参与运算的尾数应先还原为:

1.010100...

如果它的阶码比另一个数小 3,就要右移 3 位:

1.010100... 右移 3 位
= 0.001010100...

所以隐藏的 1 会参与右移,而不是永远固定在最高位。

逐项判断:

A 错。隐藏的 1 不会始终保留在最高位,对阶右移时它可能被移到小数点后更低的位置。

B 错。不能为了“保持规格化形式”在对阶后强行把最高位重置为 1。对阶阶段只是为了统一阶码,不要求每个参与运算的临时尾数仍然保持规格化。

C 对。规格化数在参与尾数运算前,应先把隐含的前导 1 恢复出来,然后整体右移。

D 错。非规格化数没有隐含的前导 1,它的有效数是 0.M,不是 1.M

因此第16题选 C


第17题:阶码差多大时,小数可以直接忽略?

本题选 B:25

IEEE 754 单精度浮点数的有效位数是:

1 位隐藏位 + 23 位尾数字段 = 24 位有效位

也就是说,单精度浮点数的精度是 24 位二进制有效数字,而不是 23 位。23 位只是显式存储的尾数字段,隐藏的前导 1 也要算进精度。

浮点加减法对阶时,阶码小的数要右移。假设两个数的阶码差为 ΔE:

ΔE = |E1 - E2|

如果 ΔE 很大,阶码较小的数右移太多,它的有效位会被移到结果保留精度之外,对最终结果没有影响。

单精度有 24 位有效位。采用就近舍入时,判断一个很小的数是否还能影响结果,要看它是否达到“大数最低有效位的一半”。

在单精度中,如果阶码差达到:

ΔE ≥ 25

那么小阶数最大也小于大阶数当前精度下的半个最低有效位,因此在就近舍入下不会改变最终结果,可以直接取阶码较大的那个操作数。

所以本题选 B:25

这里最容易错的是选 A:24。因为很多人记住“单精度有效位数是 24 位”,于是直接认为阶差大于等于 24 就可以忽略。但采用就近舍入时,阶差为 24 的小数仍可能正好达到半个最低有效位,可能影响舍入结果,因此不能保证无影响。

可以这样记:

单精度有效位数 p = 24
就近舍入下,阶差 ΔE ≥ p + 1 时,小数一定无影响
所以 ΔE ≥ 25

如果是双精度,则有效位数是:

1 位隐藏位 + 52 位尾数字段 = 53 位

对应的类似判断就是:

ΔE ≥ 54

三题总结

这三题本质都在考 IEEE 754 的几个固定规则。

第15题考特殊编码:

阶码全 0,尾数非 0 → 非规格化数
阶码全 0,尾数全 0 → 0
阶码全 1,尾数全 0 → 无穷大
阶码全 1,尾数非 0 → NaN

第16题考隐藏位是否参与运算:

规格化数参与运算前,要把隐藏的 1 恢复出来
对阶时,整个有效数一起右移
非规格化数没有隐藏的 1

第17题考单精度有效位数和舍入影响:

单精度有效位数 = 24 位
采用就近舍入时,阶差 ΔE ≥ 25,小阶数对结果无影响

所以最终答案为:

15. A
16. C
17. B

如何理解这句:当两浮点数阶码相差超过尾数位宽(24153位)时,小阶操作数在右移后有效位全部丢失,导致加法结果等于大阶操作数

这句话本质上是在解释浮点加法中的对阶过程,也就是为什么会出现“大数吃小数”。

先把结论放前面:

在浮点加法中,两个数相加前必须先把阶码调成一样。阶码小的那个数,其尾数要不断右移。如果两个数的阶码差太大,小数的所有有效位都会被右移到尾数位宽之外,机器保留下来的尾数中已经没有它的有效信息,于是加法结果就等于那个大数。

也就是:

大数 + 极小数 = 大数

这不是数学上相等,而是浮点数机器表示精度不够导致的结果


以 IEEE 754 double 为例,一个 double 的有效精度是 53 位二进制有效位,其中包括隐藏的最高位 1。它的存储格式是:

1 位符号位 + 11 位阶码 + 52 位尾数字段

虽然尾数字段只存 52 位,但规格化数最高位的 1 被隐藏了,所以实际有效位数是:

1 + 52 = 53 位

因此 double 能精确表示所有绝对值不超过 253 的整数。32 位 int 的范围大约是:

-2³¹ ~ 2³¹ - 1

显然远小于 253,所以任意 int 转成 double 都不会丢精度。


现在重点看“对阶时尾数右移”。

假设有两个二进制浮点数:

A = 1.000000 × 2³⁰
B = 1.000000 × 2⁰

要计算 A + B,不能直接把尾数相加,因为阶码不同。必须先把 B 改写成阶码也是 30 的形式:

B = 0.000000000000000000000000000001 × 2³⁰

也就是说,B 的尾数要向右移动 30 位。

如果机器尾数有效位足够多,那么 B 的那个 1 还能保留下来,结果还能体现出 B 的贡献。

但如果阶码差太大,比如:

A = 1.000000 × 2¹⁰⁰
B = 1.000000 × 2⁰

那么为了对阶,B 的尾数要右移 100 位:

B = 0.000000...0001 × 2¹⁰⁰

可是 double 只能保留大约 53 位有效二进制位。B 的有效位被右移 100 位以后,已经完全跑到可保留尾数范围之外了,机器保存不到它,所以它在运算中相当于变成 0。

于是:

A + B 的浮点结果 = A

这就是“小阶操作数在右移后有效位全部丢失,导致加法结果等于大阶操作数”。


可以用十进制科学计数法类比。假设机器只能保留 5 位有效数字,现在计算:

1.2345 × 10⁸ + 1.0000 × 10⁰

对阶后:

1.2345 × 10⁸
0.00000001 × 10⁸

如果只能保留 5 位有效数字,第二个数的 1 远远排在第 5 位之后,直接被舍掉,所以结果还是:

1.2345 × 10⁸

数学上它们当然不相等,但在有限精度机器里,小数被“大数吃掉”了。


回到这道题。

题中有:

int x, y, z;
sizeof(int) = 4;
double 使用 IEEE 754 双精度格式;

double dx = (double)x;
double dy = (double)y;
double dz = (double)z;

说法 I 是:

dx + dy == (double)(x + y)

这个不永真。原因不是 double 加法有问题,而是右边的 x + y 先按照 int 运算。如果 x + y 在 32 位 int 中溢出,那么右边得到的是溢出后的 int 结果,再转换成 double;而左边是先把 x、y 转成 double,再相加,不会发生 int 溢出。所以两边可能不同。

比如直观理解:

x = 2147483647
y = 1

左边:

dx + dy = 2147483648.0

右边:

(int)(x + y) 先发生 32 位有符号整数溢出

因此结果可能不等。


说法 II 是:

(dx + dy) + dz == dx + (dy + dz)

这道题里它永真,关键原因是:dx、dy、dz 都来自 32 位 int,而 double 的精度足够大,能够精确表示它们以及它们两两相加、三者相加的结果。

最大情况下,三个 32 位 int 的和的绝对值也只是 2³³ 量级:

|x + y + z| < 3 × 2³¹ < 2³³

而 double 能精确表示不超过 253 的整数,所以这些中间结果都不会舍入。

也就是说,这里虽然形式上是浮点加法,按一般规律浮点加法“不一定满足结合律”,但本题的操作数范围太小,double 精度足够,所有结果都能精确表示,所以:

(dx + dy) + dz

dx + (dy + dz)

都会精确得到同一个数学整数结果。


官方答案中那句“对阶时尾数移动位数不会超过 52 位,因此尾数不会舍入,不会发生大数吃小数”,意思是:

本题中的数都来自 32 位 int,转换成 double 后,指数范围大致不会超过 31 或 32 这个量级。即使中间结果参与下一次加法,也只到 33 位左右。而 double 有 53 位有效精度,所以小阶数在对阶右移时,不会被整体移出有效尾数范围。它的有效位还能被保留下来,因此不会出现“大数 + 小数 = 大数”的精度吞噬现象。

注意这里更严谨地说,double 的有效尾数位宽应理解为 53 位,而不是单纯的 52 位。52 位是显式存储的尾数字段,规格化数还隐含了最高位 1。408 做题时经常要记住:

float:有效精度 24 位 = 隐藏位 1 位 + 尾数字段 23 位
double:有效精度 53 位 = 隐藏位 1 位 + 尾数字段 52 位

所以那句中的“24153位”应该理解为“float 是 24 位有效精度,double 是 53 位有效精度”。

这类题的快速判断方法是:先看是不是整数先溢出,再看 double 能否精确表示中间整数结果。只要题中是 32 位 int 转 double,三个以内的 int 相加都远小于 253,因此 double 加法不会丢整数精度,结合律在这个特定范围内成立。

21~29 题答案依次是:

题号答案
21A
22D
23A
24D
25B
26D
27B
28A
29D

下面逐题看。

21 题:二维 short 数组、小端、行优先存储

题目给出:

static short b[2][4] = {{2,9,-1,5},{3,1,-6,2}};

short 占 2 字节,二维数组在 C 语言中按行优先存储,即存储顺序为:

b[0][0], b[0][1], b[0][2], b[0][3], b[1][0], b[1][1], b[1][2], b[1][3]

首地址是 0x8049820,题目问地址 0x804982c 中的内容。

先算偏移量:

0x804982c - 0x8049820 = 0xC = 12 字节

每个 short 占 2 字节,所以这是第:

12 / 2 = 6

个 short 元素,从 0 开始编号,编号 6 对应:

b[1][2]

而:

b[1][2] = -6

16 位补码中:

-6 = FFFAH

小端方式存储时,低字节放在低地址,高字节放在高地址:

地址 0x804982c:FAH
地址 0x804982d:FFH

所以本题选:

A. FAH

这题的关键是三步:先行优先定位元素,再把负数写成补码,最后按小端判断低地址内容。

22 题:int 数据的小端存储

题目给出:

int i 的地址为 08000000H
i 的机器数为 01234567H

int 占 4 字节,机器数:

01 23 45 67

这是按从高字节到低字节写出来的逻辑表示。小端方式存储时,最低有效字节放在最低地址。

所以内存中为:

08000000H:67H
08000001H:45H
08000002H:23H
08000003H:01H

题目问地址 08000000H 单元的内容,因此为:

67H

答案是:

D. 67H

这类题只要记住一句话:小端,低地址存低字节。

第22题里面最低有效字节是怎么理解的?把它当数的结尾

可以把“最低有效字节”粗略理解成“这个数写成十六进制时最右边的那 1 个字节”,也就是你说的“数的结尾”。但更准确地说,它不是“视觉上的结尾”,而是权值最低、对数值影响最小的那个字节

以第 22 题为例:

i 的机器数 = 01234567H

这是一个 32 位 int,占 4 字节。每 2 个十六进制位是 1 字节,所以可以拆成:

01   23   45   67

从数值权值看:

01234567H = 01H × 16^6 + 23H × 16^4 + 45H × 16^2 + 67H × 16^0

其中 67H 乘的是 16^0,权值最低,所以 67H 就是最低有效字节

对应地:

01H:最高有效字节
67H:最低有效字节

也可以换成十进制类比。比如十进制数:

123456

其中最右边的 6 是个位,权值最低;最左边的 1 是十万位,权值最高。十六进制数也是一样:

01234567H

最右边的 67H 是最低的 1 字节,类似“个位所在的部分”。

所以对于:

01234567H

拆字节后:

高字节                                  低字节
01H          23H          45H          67H
最高有效字节                         最低有效字节

“小端方式”的规则是:

低地址存放最低有效字节
高地址存放最高有效字节

因此内存中是:

地址 08000000H:67H
地址 08000001H:45H
地址 08000002H:23H
地址 08000003H:01H

所以第 22 题问地址 08000000H 单元内容,就是 67H

考研题里可以直接记成一句话:

数写成十六进制后,最右边的两个十六进制位就是最低有效字节。
小端:最低有效字节放低地址。
大端:最高有效字节放低地址。

所以你说“把它当数的结尾”基本可以这样理解,但最好再补一句:这个“结尾”指的是按通常十六进制书写时最右边的字节,也就是权值最低的字节

23 题:结构体边界对齐

结构体为:

struct cont_info {
    char id;
    unsigned post;
    char phone;
} x;

在 32 位机器中,通常:

char 占 1 字节,按 1 字节对齐
unsigned int 占 4 字节,按 4 字节对齐

假设 x 的首地址是:

0x8049820

按边界对齐分配:

offset 0:char id,占 1 字节
offset 1~3:填充 3 字节,使 post 从 4 字节边界开始
offset 4~7:unsigned post,占 4 字节
offset 8:char phone,占 1 字节

所以 phone 的起始偏移量是 8。

地址为:

0x8049820 + 8 = 0x8049828

答案是:

A. 0x8049828

这题容易错在以为 char id 后面立刻接 unsigned post,得到 0x80498250x8049826。但结构体成员要满足边界对齐,unsigned 不能随便放在任意地址。

24 题:C 语言类型转换与浮点精度

题目给出:

int i = 12345
float f = 1.2345 × 2^3

即:

f = 9.876

问哪个表达式结果为“假”。

逐项判断。

A:

i == (int)(double)i

intdouble,再转回 intdouble 有 53 位有效二进制位,可以精确表示 32 位 int 中的所有整数,所以 12345 不会丢失精度。A 为真。

B:

f == (float)(double)f

f 本来就是一个 float 值。floatdouble 是精确转换,再转回 float,仍然是原来的 float 值。B 为真。

C:

i == (int)(float)i

float 有 24 位有效二进制位,可以精确表示绝对值不超过 2^24 的整数。12345 远小于 2^24,所以 int -> float -> int 不会改变值。C 为真。

D:

f == (float)(int)f

f = 9.876,强制转换为 int 会截断小数部分,得到:

(int)f = 9

再转为 float:

(float)(int)f = 9.0

显然:

9.876 != 9.0

所以 D 为假。

答案是:

D

这类题要抓住两个点:整数转 float 是否超过 2^24,浮点数转 int 会截断小数部分。

25 题:C 表达式求值与整数除法

代码为:

int m = 13;
float a = 12.6, x;
x = m / 2 + a / 2;
printf("%f\n", x);

重点在:

m / 2

因为 mint2 也是 int,所以这里是整数除法:

13 / 2 = 6

不是 6.5

再看:

a / 2

afloat,所以 2 会转换为 float,做浮点除法:

12.6 / 2 = 6.3

所以:

x = 6 + 6.3 = 12.3

printf("%f\n", x) 默认输出 6 位小数,所以输出为:

12.300000

答案是:

B. 12.300000

这题最容易错选 12.800000,原因是把 m / 2 错当成了 6.5

26 题:浮点加法、规格化与阶码溢出

题目给出:

X = 2^7 × 29/32
Y = 2^5 × 5/8

阶码和尾数均采用补码表示,阶码 5 位、尾数 7 位,并且都包含 2 位符号位。

先把尾数写成二进制:

29/32 = 0.11101₂
5/8 = 0.101₂

所以:

X = 2^7 × 0.11101₂
Y = 2^5 × 0.101₂

做浮点加法,先对阶。X 的阶码是 7,Y 的阶码是 5,所以要把 Y 的阶码提高到 7,尾数右移 2 位:

Y = 2^7 × 0.00101₂

尾数相加:

  0.11101
+ 0.00101
= 1.00010

这个结果尾数不符合规格化要求,需要右规一位:

1.00010 × 2^7 = 0.10001 × 2^8

所以如果只看数值,结果应该是:

2^8 × 0.10001

对应选项 C 中的形式:

01000 0010001

但本题不能选 C,因为题目明确说阶码 5 位,并且包含 2 位符号位。也就是说阶码采用变形补码,合法的正阶码最高只能到:

00111,即 +7

而结果需要阶码:

+8

其 5 位形式为:

01000

前两位符号位是 01,不是合法的 00,这表示阶码正溢出。

所以最终结果是:

发生溢出

答案是:

D

这题的坑非常典型:只做数值计算会得到 C,但考点在“双符号位补码判断溢出”。结果需要阶码 8,而本题阶码最大只能表示到 7,因此溢出。

我想知道在26题里面,29/32和5/8这个尾数是如何写成二进制的?他到二进制是应该如何转换?以及提到阶码只有五位,但是你需要八位的解码,那为什么还要在后续判断其五位的形式中前两位呢?

26 题这里有两个点容易混在一起:一个是真值小数怎么转二进制尾数,另一个是双符号位补码怎么判断阶码溢出。分开看就清楚了。

1. 29/32 怎么写成二进制尾数?

因为分母 32 = 2^5,所以这是最容易转二进制的一类分数。

二进制小数的各位权值是:

0.1₂     = 1/2
0.01₂    = 1/4
0.001₂   = 1/8
0.0001₂  = 1/16
0.00001₂ = 1/32

所以只要把分子 29 写成 5 位二进制即可:

29 = 16 + 8 + 4 + 1
   = 11101₂

于是:

29/32 = 0.11101₂

也可以这样理解:

0.11101₂
= 1/2 + 1/4 + 1/8 + 0/16 + 1/32
= 16/32 + 8/32 + 4/32 + 0/32 + 1/32
= 29/32

所以:

X = 2^7 × 29/32
  = 2^7 × 0.11101₂

这里的 0.11101₂ 就是 X 的尾数真值。

2. 5/8 怎么写成二进制尾数?

同理,8 = 2^3,所以只要把分子 5 写成 3 位二进制:

5 = 101₂

于是:

5/8 = 0.101₂

也可以按权值验证:

0.101₂
= 1/2 + 0/4 + 1/8
= 4/8 + 0/8 + 1/8
= 5/8

因为题目说尾数一共 7 位,并且含 2 位符号位,所以尾数的小数部分实际还有 5 位。为了和 X 的尾数位数对齐,通常写成:

5/8 = 0.10100₂

后面补 0 不改变数值。

所以:

Y = 2^5 × 5/8
  = 2^5 × 0.10100₂

3. 通用方法:十进制小数转二进制小数

对于 29/325/8 这种分母是 2 的幂的分数,直接把分子转二进制最方便。

但如果遇到一般小数,比如 0.625,可以用“乘 2 取整法”:

0.625 × 2 = 1.25   取整数部分 1
0.25  × 2 = 0.5    取整数部分 0
0.5   × 2 = 1.0    取整数部分 1

所以:

0.625 = 0.101₂

因为:

0.101₂ = 1/2 + 0/4 + 1/8 = 0.625

这类题中,最常见的是分母为 2^n 的分数。快速做法就是:

a / 2^n = 0.(a 的 n 位二进制表示)₂

例如:

29/32 = 29 / 2^5 = 0.11101₂
5/8   = 5 / 2^3  = 0.101₂

4. 为什么 X + Y 之后要对阶?

现在有:

X = 2^7 × 0.11101₂
Y = 2^5 × 0.10100₂

浮点加法不能直接加尾数,必须先把阶码调成一样。一般向大阶对齐,所以把 Y 的阶码从 5 调到 7。

阶码增加 2,尾数就要右移 2 位:

Y = 2^5 × 0.10100₂
  = 2^7 × 0.00101₂

于是:

X + Y
= 2^7 × 0.11101₂ + 2^7 × 0.00101₂
= 2^7 × (0.11101₂ + 0.00101₂)

尾数相加:

  0.11101
+ 0.00101
= 1.00010

结果尾数变成了 1.00010₂,这不是规格化的小数尾数形式,所以要右规。

右规一位:

1.00010₂ × 2^7 = 0.10001₂ × 2^8

所以从数学真值看,结果应该是:

2^8 × 0.10001₂

5. “阶码只有 5 位,为什么还要看前两位?”

这里不是说“需要 8 位阶码”,而是说结果的阶码真值需要等于 +8

也就是说:

结果阶码真值 = +8

但题目规定:

阶码位数为 5 位,且含 2 位符号位

这叫“双符号位补码”或“变形补码”。它不是普通 5 位补码那样可以表示到 +15,而是用最高两位一起作为符号判断。

对于 5 位双符号位补码,结构可以理解为:

符号位 符号位 数值位 数值位 数值位

也就是:

S1 S2 xxx

合法结果要求两个符号位相同:

00:正数,未溢出
11:负数,未溢出
01:正溢出
10:负溢出

因为 5 位里面有 2 位符号位,真正用于表示数值范围的只有后面 3 位,所以阶码能表示的合法正数范围是:

00000 = 0
00001 = 1
00010 = 2
00011 = 3
00100 = 4
00101 = 5
00110 = 6
00111 = 7

最大合法正阶码是:

+7 = 00111

但是本题计算后需要:

阶码 = +8

如果硬把 +8 写成 5 位二进制形式,就是:

01000

现在看最高两位:

01

双符号位为 01,这不是合法正数的 00,而是正溢出

所以本题最终不能写成:

01000 0010001

虽然它从“数值推导”上看像是 2^8 × 0.10001₂,但阶码部分 01000 已经溢出了。因此答案是:

发生溢出

6. 这里的关键区别

你问的点本质上是这个:

“阶码真值为 +8” ≠ “阶码需要 8 位”

本题不是说要用 8 位来存阶码,而是计算结果的指数变成了 +8

但是题目只给阶码 5 位,并且其中还有 2 位符号位,所以它的最大合法正阶码只能到 +7。当结果需要 +8 时,编码成 5 位会出现:

01000

前两位是 01,说明发生正溢出。

所以判断逻辑是:

先算出结果需要阶码 +8
再检查 +8 能不能用题目规定的 5 位双符号位补码合法表示
发现 01000 的双符号位为 01
因此阶码正溢出

这就是为什么还要看五位形式的前两位。因为“双符号位补码”的溢出判断就是看最高两位是否相同。

27 题:int、float、double 转换关系判断

题目给出:

int i = -785
float f = 1.5678E3
double d = 1.5E100

即:

f = 1567.8

判断四个表达式哪些为真。

I:

i == (int)(float)i

i = -785,其绝对值远小于 2^24,float 可以精确表示这个整数。

所以:

(int)(float)(-785) = -785

I 为真。

II:

f == (float)(int)f

f = 1567.8,先转 int 会截断小数部分:

(int)f = 1567

再转 float:

(float)(int)f = 1567.0

而原来的 f 约为 1567.8,两者不相等。II 为假。

III:

f == (float)(double)f

floatdouble 是精确的,因为 double 精度更高;再转回 float,仍然得到原来的 float 值。

所以 III 为真。

IV:

(d + f) - d == f

d = 1.5E100 非常大,f = 1567.8 相比它极小。

在浮点加法中,大数和小数相加时,如果阶码差太大,小数会在对阶右移过程中被完全丢失。因此:

d + f ≈ d

于是:

(d + f) - d ≈ d - d = 0

不等于 f。IV 为假。

所以为真的只有 I 和 III。

答案是:

B. 仅 I 和 III

这题考查的是两个经典结论:float 能精确表示 24 位以内的整数;大浮点数加很小的浮点数时,小数可能被“吃掉”。

27题里面因为无论是int还是float和double型,它们的数位都是很大的,所以说它们的范围也会跟着大,但所以我就想知道它在这里是不是跟之前学的补码源码联系已经不大了,那我们之前学的补码反码跟它有什么关系呢?

可以这样理解:**第 27 题主要已经不是在考“补码、反码、原码之间的编码转换”,而是在考 C 语言数值类型转换 + IEEE 754 浮点数的范围和精度。**但是它和前面学的补码、移码并不是完全没关系,而是关系变成了“底层表示方式不同,各管各的”。

也就是说,int 仍然通常用补码表示;floatdouble 不用补码直接表示整个数,而是用 IEEE 754 的“符号位 + 阶码 + 尾数”表示。两者之间发生强制类型转换时,不是把二进制位原封不动拿过去解释,而是把“数值”转换成另一种编码形式。

1. int、float、double 的“范围大”不是同一个原因

容易混淆的一点是:看到 intfloatdouble 都是“很多位”,就以为它们只是位数多少的问题。实际上不是。

对于 32 位 int,如果是补码表示,它的 32 位基本都用来表示整数值,所以范围大约是:

-2^31 ~ 2^31 - 1

它的特点是:范围不算特别大,但在范围内每一个整数都能精确表示。

例如:

0, 1, 2, 3, ..., 12345, ..., 2147483647

这些整数只要在范围内,都可以逐个精确表示。

但 32 位 float 不是这样。IEEE 754 单精度 float 是:

1 位符号位 + 8 位阶码 + 23 位尾数

因为它有阶码,所以它可以表示类似:

1.01₂ × 2^100
1.11₂ × 2^-50

这种非常大或非常小的数。因此 float 的范围比 32 位 int 大得多。

但是代价是:float 只有 24 位有效二进制精度,其中包括隐藏位 1。也就是说,float 不能精确表示所有 32 位整数。

所以要区分:

范围:能表示多大、多小
精度:能表示得多细、多准

float 的范围很大,但精度有限;int 的范围相对小,但范围内整数逐个精确。

这是第 27 题的核心背景。

2. 补码、原码、反码主要管整数;IEEE 754 管浮点数

前面学的原码、反码、补码,主要是为了表示定点整数,尤其是有符号整数。

例如 32 位 int i = -785,在机器里通常用补码存储。它的本质是:

CPU 中的 int 负数通常按补码存放
补码适合整数加减法运算

但是 float f = 1.5678E3double d = 1.5E100 不是用补码表示整个数的。

IEEE 754 浮点数更接近这种形式:

(-1)^符号位 × 有效数 × 2^阶码

单精度 float:

符号位 S:表示正负
阶码 E:用移码/偏置形式表示
尾数 M:表示有效数字

所以对 float 来说,负数不是像补码那样“全部取反加一”。比如 -8.25 的 float 表示,只是符号位变成 1,阶码和尾数仍然表示 8.25 的规格化形式。

这和整数补码完全不同。

所以可以记成:

int:重点看补码
float/double:重点看 IEEE 754,符号位 + 阶码 + 尾数

3. float/double 里面和“移码”有关系

虽然 float/double 不用补码表示整个数,但它的阶码和之前学的“移码”有联系。

IEEE 754 单精度阶码字段有 8 位,偏置值是 127。阶码真值 E 和阶码字段 e 的关系是:

e = E + 127
E = e - 127

例如 8.25 = 1.00001₂ × 2^3,阶码真值是 3,所以单精度阶码字段是:

3 + 127 = 130 = 10000010₂

这本质上就是偏置表示,和前面学的“移码”思想很接近。

所以前面学的内容不是没用,而是分工变了:

补码:主要用于有符号整数
移码:常用于浮点数阶码
原码思想:浮点数的符号位 + 数值部分有点类似原码思想

但 IEEE 754 的浮点数整体不能按补码理解。

4. C 语言强制类型转换不是“二进制位直接换解释方式”

这是第 27 题特别容易错的地方。

比如:

i == (int)(float)i

这里不是把 i 的补码位模式直接当成 float 看。

而是:

int 数值 -785
先转换成 float 数值 -785.0
再转换回 int 数值 -785

所以这个过程是“数值转换”,不是“位模式重解释”。

如果是位模式重解释,那就是另一类问题,比如用指针强转、联合体、内存查看等,这种不是第 27 题考点。

第 27 题考的是:

这个数值转过去会不会超范围?
会不会丢精度?
再转回来是否还能相等?

5. 回到第 27 题:为什么 I 真?

题目中:

int i = -785

表达式 I:

i == (int)(float)i

判断过程是:

-785 是 int,机器中通常以补码存储
(float)i 把数值 -785 转成 float
float 能精确表示 -785
再转回 int 仍然是 -785

为什么 float 能精确表示 -785

因为 float 有 24 位有效二进制精度。所有绝对值不超过 2^24 的整数,float 都可以精确表示。

785 << 2^24

所以:

(int)(float)(-785) = -785

因此 I 为真。

这里虽然 i 在机器里是补码,但判断是否相等时,重点不是补码怎么写,而是:

这个 int 数值转成 float 会不会丢精度

6. 为什么 II 假?

表达式 II:

f == (float)(int)f

题目给:

f = 1.5678E3 = 1567.8

执行:

(int)f

浮点数转整数会截断小数部分,得到:

1567

再转回 float:

(float)1567 = 1567.0

而原来的 f 是大约 1567.8,所以:

1567.8 != 1567.0

因此 II 为假。

这里跟补码几乎没关系,考的是:

float 转 int 会丢掉小数部分

7. 为什么 III 真?

表达式 III:

f == (float)(double)f

f 本来是一个 float 值。先把 float 转成 double:

float -> double

double 的有效精度比 float 高,所以这个转换不会丢失原来的 float 值。

然后再从 double 转回 float:

double -> float

因为这个 double 值本来就是由某个 float 精确转换来的,所以再转回 float 还是原来的 float。

所以 III 为真。

这类题可以记成:

float -> double -> float,一般保持不变
int 小整数 -> float -> int,一般保持不变
float 带小数 -> int -> float,一般改变

8. 为什么 IV 假?

表达式 IV:

(d + f) - d == f

题目给:

d = 1.5E100
f = 1567.8

d 极大,f 相比 d 极小。

做浮点加法时,要先对阶。两个数阶码差非常大,小数 f 在对阶右移过程中会被完全“吃掉”。

也就是:

d + f ≈ d

因此:

(d + f) - d ≈ d - d = 0

不等于 f

这里也不是补码问题,而是浮点数精度问题。double 的范围很大,可以表示 1.5E100,但它在这么大的数量级附近,两个相邻可表示数之间的间隔也非常大。1567.8 对它来说太小,加上去没有任何可见变化。

所以 IV 为假。

9. 和补码、原码、反码的关系可以这样总结

前面学的补码、原码、反码不是没用,而是在不同章节中承担不同作用。

对于整数运算题:

有符号整数范围
加减法溢出
符号扩展
算术右移
CF/OF 标志
int、short、char 的存储

这些都强依赖补码。

对于浮点数题:

IEEE 754 格式
符号位、阶码、尾数
规格化
隐藏位
阶码偏置
浮点数舍入
float/double 类型转换
大数吃小数

这些主要看 IEEE 754,不再把整个数当补码处理。

但是浮点数中仍然有前面知识的影子:

内容在整数中在浮点数中
符号补码最高位参与数值编码单独的符号位
负数表示补码表示,取反加一符号位为 1,阶码尾数表示绝对值
阶码整数没有阶码通常用偏置/移码思想
精度范围内整数逐个精确只保留有限有效位,可能舍入
溢出超出整数范围阶码超范围或结果变成无穷大

所以最稳的理解是:

补码解决的是整数机器数问题;
IEEE 754 解决的是浮点数机器数问题;
类型转换题考的是两种机器数体系之间的数值转换,而不是二进制位直接解释。

10. 考研中遇到这类题怎么快速判断

看到 int、float、double 混合比较时,不要先去写补码。先判断这几个问题:

第一,看是不是整数转浮点。
如果是 int -> float -> int,重点看整数绝对值是否超过 2^24。不超过,一般能精确恢复;超过,就可能丢精度。

第二,看是不是 int -> double -> int
double 有 53 位有效二进制精度,所以 32 位 int 都能被 double 精确表示。考研里常用结论是:

double 可精确表示所有 32 位 int

第三,看是不是浮点转整数。
例如:

(float)(int)f

只要 f 带小数部分,转 int 就会截断,小数丢失,一般不相等。

第四,看是不是 float 和 double 互转。
如果是:

(float)(double)f

其中 f 原本就是 float,那么通常为真。

第五,看是不是大数加小数。
例如:

(d + f) - d == f

这种经常是假的,因为浮点加法不满足普通数学中的结合律、消去律。大数加小数时,小数可能被对阶右移丢掉。

这就是第 27 题的主线。它和补码有联系,但不是主要矛盾。此题真正考的是:整数补码体系和 IEEE 754 浮点体系之间做数值转换时,范围、精度和舍入如何影响结果。

28 题:-8.25 的 IEEE 754 单精度表示

IEEE 754 单精度格式为:

1 位符号位 + 8 位阶码 + 23 位尾数

现在:

x = -8.25

先把绝对值转成二进制:

8.25 = 1000.01₂

规格化:

1000.01₂ = 1.00001₂ × 2^3

符号位:

S = 1

阶码真值:

E = 3

单精度偏置值是 127,所以移码阶码为:

3 + 127 = 130

130 的二进制是:

10000010

尾数字段只保存小数部分,即隐藏最高位 1 后面的部分:

00001 后面补 0 到 23 位

所以整体为:

1 10000010 00001000000000000000000

按 4 位一组转十六进制:

1100 0001 0000 0100 0000 0000 0000 0000

即:

C1040000H

答案是:

A. C1040000H

这题注意:题目问的是浮点寄存器 FR1 的内容,不是内存中的字节排列,所以不需要考虑大小端。

28题里面,你提到了单精度的偏移值,那你能给我讲一下,就是说在这些精度类型数据里面,它的偏移值应该是多少呢?这种该怎么求呢?还是说需要直接记住这玩意,需要背下来

在第 28 题里提到的“偏移值”,也叫偏置值阶码偏置Bias。它主要用于 IEEE 754 浮点数的阶码字段。

先给结论:

IEEE 754 中,若阶码字段有 k 位,则偏置值 Bias = 2^(k-1) - 1

常见精度需要记住:

类型总位数符号位阶码位数 k尾数字段位数偏置值 Bias
半精度 half16 位151015
单精度 float32 位1823127
双精度 double64 位111521023

中国计算机考研里最常考的是 IEEE 754 单精度 float,其次是 双精度 double。所以 1271023 最好直接记住。

1. 偏置值是干什么的?

IEEE 754 浮点数大致表示为:

(-1)^S × 1.M × 2^E

其中:

S:符号位
M:尾数部分
E:阶码真值,也就是实际指数

问题在于,指数 E 可能是正数,也可能是负数。

例如:

8.25 = 1.00001₂ × 2^3

这里阶码真值是:

E = +3

而:

0.125 = 1.0₂ × 2^(-3)

这里阶码真值是:

E = -3

如果直接用补码存阶码也可以,但 IEEE 754 没这么做。它采用的是偏置表示法,也可以理解为一种移码思想:

阶码字段值 = 阶码真值 + 偏置值

也就是:

e = E + Bias

反过来:

E = e - Bias

这里:

E:阶码真值
e:机器中阶码字段的无符号值
Bias:偏置值

2. 单精度 float 的偏置值为什么是 127?

IEEE 754 单精度 float 一共 32 位:

1 位符号位 + 8 位阶码 + 23 位尾数

阶码字段有 8 位,所以:

Bias = 2^(8-1) - 1
     = 2^7 - 1
     = 128 - 1
     = 127

所以单精度浮点数中:

阶码字段值 = 阶码真值 + 127

例如第 28 题:

x = -8.25

先看绝对值:

8.25 = 1000.01₂
     = 1.00001₂ × 2^3

所以阶码真值是:

E = 3

单精度偏置值是 127,因此阶码字段值为:

e = E + 127
  = 3 + 127
  = 130

130 写成 8 位二进制:

130 = 10000010₂

所以第 28 题中的阶码字段就是:

10000010

3. 双精度 double 的偏置值为什么是 1023?

IEEE 754 双精度 double 一共 64 位:

1 位符号位 + 11 位阶码 + 52 位尾数

阶码字段有 11 位,所以:

Bias = 2^(11-1) - 1
     = 2^10 - 1
     = 1024 - 1
     = 1023

所以双精度中:

阶码字段值 = 阶码真值 + 1023

例如某个 double 数规格化后是:

1.01₂ × 2^5

那么:

E = 5
e = 5 + 1023 = 1028

再把 1028 写成 11 位二进制,就是它的阶码字段。

4. 这个偏置值需要背吗?

需要分两层看。

第一层,公式要理解:

k 位阶码的 IEEE 754 偏置值 = 2^(k-1) - 1

第二层,常见类型要直接记:

float:阶码 8 位,偏置 127
double:阶码 11 位,偏置 1023

考研做题时,不建议每次都从头推。看到 IEEE 754 单精度,应该马上反应:

单精度:1 + 8 + 23,偏置 127

看到 IEEE 754 双精度,应该马上反应:

双精度:1 + 11 + 52,偏置 1023

这样做题速度会快很多。

5. 为什么偏置值不是 2^(k-1),而是 2^(k-1)-1?

这个地方容易和前面学的“移码”混。

在一般教材里,n 位移码有时定义为:

移码 = 真值 + 2^(n-1)

比如 8 位移码偏置可能写成 128。

但是 IEEE 754 的阶码偏置采用的是:

Bias = 2^(k-1) - 1

所以 8 位阶码不是偏置 128,而是偏置 127。

为什么这么设计?简单从考研角度理解即可:IEEE 754 把阶码字段的全 0 和全 1 留作特殊用途。

对于单精度:

阶码字段 00000000:用于 0 和非规格化数
阶码字段 11111111:用于无穷大和 NaN

真正用于规格化数的阶码字段是:

00000001 ~ 11111110

也就是十进制:

1 ~ 254

用偏置 127 后,规格化数的阶码真值范围为:

最小规格化阶码真值 = 1 - 127 = -126
最大规格化阶码真值 = 254 - 127 = +127

这就是单精度规格化数的阶码真值范围:

-126 ~ +127

对于双精度:

阶码字段全 0:特殊
阶码字段全 1:特殊
规格化阶码字段范围:1 ~ 2046
Bias = 1023
阶码真值范围:-1022 ~ +1023

这些范围在浮点数最大值、最小规格化正数题里经常用。

6. 单精度 float 的阶码范围怎么来的?

以单精度为例:

阶码字段 8 位
Bias = 127

8 位无符号字段本来能表示:

0 ~ 255

但 IEEE 754 中:

0:保留给 0 和非规格化数
255:保留给无穷大和 NaN

所以规格化数的阶码字段只能取:

1 ~ 254

转换成阶码真值:

E = e - 127

所以:

e = 1   时,E = 1 - 127 = -126
e = 254 时,E = 254 - 127 = +127

因此单精度规格化数阶码真值范围是:

-126 ~ +127

这也解释了第 29 题为什么最大有限正数的阶码真值是 +127,而不是 +128

因为阶码字段全 1,即 255,不能用于普通有限数。

7. 常见题型怎么用这个偏置值?

第一类:给十进制数,求 IEEE 754 表示

例如:

-8.25

做法是:

8.25 = 1000.01₂ = 1.00001₂ × 2^3

得到:

符号位 S = 1
阶码真值 E = 3
单精度阶码字段 e = 3 + 127 = 130 = 10000010₂
尾数 = 00001 后补 0 到 23 位

所以整体:

1 10000010 00001000000000000000000

再转十六进制:

C1040000H

第二类:给 IEEE 754 机器数,求真值

例如阶码字段为:

10000010₂

先转十进制:

10000010₂ = 130

单精度偏置值为 127:

E = 130 - 127 = 3

所以这个浮点数的指数部分就是:

2^3

然后再结合符号位和尾数求真值。

第三类:求最大正数、最小规格化正数

单精度最大有限正数:

阶码字段最大不能取 255,只能取 254
阶码真值 E = 254 - 127 = 127
尾数取最大:1.111...111₂ = 2 - 2^(-23)

所以最大有限正数为:

(2 - 2^(-23)) × 2^127
= 2^128 - 2^104

单精度最小规格化正数:

阶码字段最小规格化取 1
阶码真值 E = 1 - 127 = -126
尾数最小规格化取 1.0

所以:

1.0 × 2^(-126)

8. 和前面“移码”的关系

你可以把 IEEE 754 的阶码偏置理解为“移码思想”,但要注意它的具体偏置值和普通教材里讲的 n 位移码不完全一样。

考研中建议这样区分:

普通移码题:按题目给定规则,常见偏置为 2^(n-1)
IEEE 754 浮点数阶码:固定用 Bias = 2^(k-1) - 1

所以不要机械地把所有“移码”都当成同一个偏置。

IEEE 754 单精度阶码 8 位,偏置是:

127

不是:

128

这是高频易错点。

9. 最后整理成做题记忆版

遇到 IEEE 754 单精度,直接记:

float:32 位
1 位符号位
8 位阶码,偏置 127
23 位尾数,隐藏最高位 1
阶码真值 E = 阶码字段值 - 127
规格化阶码真值范围:-126 ~ +127

遇到 IEEE 754 双精度,直接记:

double:64 位
1 位符号位
11 位阶码,偏置 1023
52 位尾数,隐藏最高位 1
阶码真值 E = 阶码字段值 - 1023
规格化阶码真值范围:-1022 ~ +1023

偏置值本身可以用公式求:

Bias = 2^(阶码位数 - 1) - 1

但考试中,float = 127double = 1023 最好直接背下来。单精度是组成原理浮点数题的重点,看到 IEEE 754 单精度 应该立刻想到 1 + 8 + 23,偏置 127

29 题:IEEE 754 单精度能表示的最大正整数

IEEE 754 单精度浮点数:

阶码字段 8 位
尾数字段 23 位
偏置值 127

最大有限正数不能使用全 1 阶码,因为阶码全 1 用于表示无穷大和 NaN。

所以最大有限数的阶码字段是:

254

对应的阶码真值为:

254 - 127 = 127

尾数部分取最大,即 23 位全为 1:

1.111...111₂

这个有效数等于:

2 - 2^(-23)

所以最大正有限数为:

(2 - 2^(-23)) × 2^127

展开:

2 × 2^127 - 2^(-23) × 2^127
= 2^128 - 2^104

这个数本身也是整数,因此它就是 float 能表示的最大正整数。

答案是:

D. 2^128 - 2^104

这题的常见误区是只看到最大阶码真值为 127,就误以为最大值在 2^127 附近。实际上有效数最大接近 2,所以整体最大接近 2^128

阶码本质上就是“二进制科学计数法里的指数”

浮点数里的“阶码”,可以先类比十进制科学计数法理解。

比如十进制中:

12345 = 1.2345 × 10^4
0.0012345 = 1.2345 × 10^(-3)

这里的 4-3 就是指数。它不负责表示有效数字本身,而是负责说明“小数点应该移动多少位”。

IEEE 754 浮点数也是类似思想,只不过它用的是二进制:

8.25 = 1000.01₂ = 1.00001₂ × 2^3
0.125 = 0.001₂ = 1.0₂ × 2^(-3)

这里的 3-3 就是浮点数的阶码真值,也就是实际指数。

所以一句话概括:

阶码 = 二进制浮点数中 2 的指数,用来决定数值整体放大或缩小多少倍。

在 IEEE 754 中,规格化浮点数大致表示为:

数值 = (-1)^符号位 × 1.尾数 × 2^阶码真值

其中:

符号位:决定正负
尾数:决定有效数字,也就是精度
阶码:决定小数点位置,也就是数量级和范围

为什么 float 要有阶码?

如果没有阶码,只靠固定的小数点位置来表示数,那就是定点数。定点数的范围和精度分配是死的。

比如假设一个数格式固定为:

xxxxx.xxxxx

那它只能在一个固定范围内表示数。太大的数表示不了,太小的小数也表示不了。

浮点数的优势就在于:小数点可以“浮动”。而控制小数点浮动位置的,就是阶码。

例如同一个尾数 1.01₂,配不同阶码:

1.01₂ × 2^0 = 1.25
1.01₂ × 2^3 = 10.0₂ = 10
1.01₂ × 2^(-2) = 0.0101₂ = 0.3125

可以看到,尾数相同,只要阶码不同,数值大小就完全不同。

所以阶码的作用就是:

阶码越大,数越大;
阶码越小,数越接近 0;
阶码决定浮点数能表示的范围。

这也是为什么 float 虽然只有 32 位,但它能表示比 32 位 int 大得多的数。因为 float 里面有 8 位阶码,可以表示很大的指数。

阶码和尾数的分工:一个管范围,一个管精度

这是浮点数最重要的理解点。

以 IEEE 754 单精度 float 为例:

float:1 位符号位 + 8 位阶码 + 23 位尾数字段

由于规格化数隐藏最高位 1,所以 float 实际有效位数是 24 位。

这 24 位有效数字决定的是:

这个数能表示得多精细、多准确。

而 8 位阶码决定的是:

这个数能表示多大、多小。

所以:

尾数决定精度;
阶码决定范围。

例如:

1.00001₂ × 2^3

其中:

1.00001₂:尾数,有效数字
3:阶码真值,表示整体乘 2^3

如果尾数不变,阶码改变:

1.00001₂ × 2^3
1.00001₂ × 2^10
1.00001₂ × 2^(-5)

这些数的“有效数字形状”差不多,但数量级差很多。

这就是浮点数的核心:用有限的有效位数,配合阶码,去覆盖非常大的数值范围。

以 -8.25 为例看阶码怎么来

第 28 题的数是:

-8.25

先不看负号,只看绝对值:

8.25 = 1000.01₂

把它规格化成 1.xxxx × 2^E 的形式:

1000.01₂ = 1.00001₂ × 2^3

这里为什么是 2^3

因为:

1.00001₂ × 2^3

相当于把二进制小数点向右移动 3 位:

1.00001₂  →  1000.01₂

所以阶码真值就是:

E = 3

因此:

-8.25 = (-1)^1 × 1.00001₂ × 2^3

对应到 IEEE 754 单精度:

符号位 S = 1
阶码真值 E = 3
尾数部分 = 00001 后面补 0

所以阶码不是凭空来的,它就是规格化之后 2 上面的那个指数。

“float 类型自动偏移 127”是什么意思?

这里要特别注意:+127 不是说数值本身要乘 127 或加 127,也不是说计算时把原数改大了。

它只是阶码字段的存储方式

阶码真值可以是正数,也可以是负数。例如:

8.25 = 1.00001₂ × 2^3       阶码真值 E = +3
0.125 = 1.0₂ × 2^(-3)       阶码真值 E = -3

但 IEEE 754 的阶码字段本身没有单独的符号位,它把阶码字段当成一个无符号数存储。为了能表示负指数,就采用偏置表示:

阶码字段值 e = 阶码真值 E + 偏置值 Bias

单精度 float 的阶码字段有 8 位,偏置值是 127,所以:

e = E + 127
E = e - 127

例如:

E = 3
e = 3 + 127 = 130

所以机器里阶码字段存的是:

130 = 10000010₂

但这个 10000010₂ 不是阶码真值本身。它只是“带偏置后的阶码字段”。

真正参与数值含义的是:

E = 130 - 127 = 3

所以看到 float 的阶码字段时,要做两步:

存储时:阶码字段 = 阶码真值 + 127
还原时:阶码真值 = 阶码字段 - 127

为什么不直接存 +3 或 -3?

因为 IEEE 754 希望阶码字段方便比较、排序和硬件处理,所以没有采用普通补码来存阶码,而是采用偏置表示。

比如单精度中:

E = -3
e = -3 + 127 = 124 = 01111100₂

E = 0
e = 0 + 127 = 127 = 01111111₂

E = 3
e = 3 + 127 = 130 = 10000010₂

这样一来,较小的阶码真值对应较小的阶码字段值,较大的阶码真值对应较大的阶码字段值。硬件比较大小时更方便。

考研中不需要深究硬件设计动机,重点记住:

IEEE 754 的阶码用偏置表示,不用补码表示。
单精度偏置 127,双精度偏置 1023。

阶码具体有什么用?看三个例子

例 1:表示较大的数

1.0₂ × 2^20

它表示:

2^20

尾数很简单,就是 1.0₂,但阶码很大,所以整体数很大。

这说明阶码可以让 float 表示很大的数。

例 2:表示很小的小数

1.0₂ × 2^(-20)

它表示:

2^(-20)

尾数还是 1.0₂,但阶码很小,所以整体数很接近 0。

这说明阶码也可以让 float 表示很小的小数。

例 3:同一个尾数,阶码不同,数值差很多

1.101₂ × 2^0
1.101₂ × 2^5
1.101₂ × 2^(-5)

它们的有效数字都是 1.101₂,但是数量级完全不同。

所以浮点数不是单纯靠尾数表示数值,而是靠:

尾数 × 2^阶码

共同表示数值。

阶码为什么会导致“大数吃小数”?

这和之前第 27 题也能联系起来。

浮点数相加时,必须先对阶。例如:

1.5E100 + 1567.8

这两个数的阶码差非常大。为了相加,小数的尾数要向右移动很多位,直到阶码和大数对齐。

但是尾数位数有限。double 虽然比 float 精度高,也只有 53 位有效二进制位。当阶码差太大时,小数右移后有效位全部被移出去,相当于消失。

所以:

d + f ≈ d

这就是“大数吃小数”。

这也说明阶码不仅决定范围,还直接影响浮点运算过程。浮点加减法的核心步骤就是:

对阶 → 尾数运算 → 规格化 → 舍入 → 判断溢出

其中“对阶”就是让两个操作数的阶码相同。

阶码和补码、移码的关系

这里容易和前面学的补码混在一起。

对于整数:

int 负数通常用补码表示

例如 -1 的 32 位 int 通常是:

FFFFFFFFH

但对于浮点数:

float/double 整体不用补码表示

浮点数是:

符号位 + 阶码 + 尾数

其中:

符号位:类似原码思想,0 正 1 负
阶码:采用偏置表示,类似移码思想
尾数:保存有效数字的小数部分

所以浮点数和前面知识的关系可以这样理解:

整数:重点是补码
浮点数:重点是符号位、阶码偏置、尾数规格化

浮点数里面的阶码和“移码”更像,而不是补码。

单精度 float 的阶码范围有什么意义?

单精度 float:

阶码字段 8 位
偏置值 127

8 位本来可以表示:

0 ~ 255

但 IEEE 754 中:

阶码字段全 0:用于 0 和非规格化数
阶码字段全 1:用于无穷大和 NaN

所以规格化数的阶码字段只能取:

1 ~ 254

阶码真值为:

E = e - 127

因此:

最小规格化阶码真值 = 1 - 127 = -126
最大规格化阶码真值 = 254 - 127 = +127

也就是说,float 规格化数大致可以有:

2^(-126) 到 2^127

这样的数量级范围。

这就是阶码的价值:它让 float 能表示极大和极小的数。

但注意,范围大不代表每个数都精确。float 的精度仍然由尾数决定。

用一句话把它串起来

IEEE 754 浮点数可以理解成二进制科学计数法:

数值 = 符号 × 有效数字 × 2^指数

对应到字段就是:

符号位:表示正负
尾数:表示有效数字
阶码:表示指数,控制小数点移动位置

而 float 的阶码字段不是直接存指数,而是存:

阶码字段 = 阶码真值 + 127

所以第 28 题中:

-8.25 = -1.00001₂ × 2^3

这里的 3 才是真正的阶码真值。因为是单精度 float,机器中阶码字段要存:

3 + 127 = 130 = 10000010₂

最后可以记成考研做题版:

先把数写成 ±1.xxxx₂ × 2^E
E 就是阶码真值
float 阶码字段 = E + 127
double 阶码字段 = E + 1023
尾数字段只存 1.xxxx₂ 中小数点后面的 xxxx,最高位 1 隐藏

阶码不是一个额外参与加减的普通整数,而是浮点数中用于表示数量级、控制小数点位置的指数。

单看文字还是不太理解,你能不能用具体的图片或者比较直观的图表形式将这些内容给联系起来,IEE754阶码字段当成一个无符号数储存,用于来表示这个小数点的位置,但是整个浮点数的正负性是由浮点数中的尾数部分来解决,但是我还是想知道这个127偏置是怎么来的?能从补码的定义里中推倒类似吗?比方说当,一个正数到达了127之后,它的下一步就变成了负数对吧? 127+1=-128

可以。这里先把一个关键误解纠正一下:IEEE 754 浮点数的正负性不是由尾数部分解决的,而是由最前面的符号位 S 决定的。

单精度 float 的结构是:

┌──────┬──────────────┬───────────────────────────────┐
│ 符号位 │   阶码字段    │            尾数字段             │
│  S   │      e       │              M                │
│ 1位  │     8位      │             23位              │
└──────┴──────────────┴───────────────────────────────┘

对应含义是:

S:决定整个浮点数正负
e:阶码字段,按无符号数存储
M:尾数字段,保存有效数字的小数部分

规格化数的真值可以理解为:

数值 = (-1)^S × 1.M × 2^E

其中:

E = e - 127

所以:

符号位 S 管正负;
尾数 M 管有效数字;
阶码 E 管小数点移动位置,也就是数量级。

1. 先用 -8.25 画一遍完整结构

以第 28 题的 -8.25 为例。

先把绝对值转成二进制:

8.25 = 1000.01₂

规格化:

1000.01₂ = 1.00001₂ × 2^3

所以:

符号位 S = 1,因为原数是负数
阶码真值 E = 3,因为小数点从 1.00001₂ 移到 1000.01₂,需要右移 3 位
尾数部分 M = 00001 后面补 0,因为最高位的 1 被隐藏

单精度 float 的偏置值是 127,所以阶码字段:

e = E + 127 = 3 + 127 = 130

130 写成 8 位二进制:

130 = 10000010₂

于是整个 float 为:

┌───┬──────────┬─────────────────────────┐
│ S │    e     │            M            │
├───┼──────────┼─────────────────────────┤
│ 1 │ 10000010 │ 00001000000000000000000 │
└───┴──────────┴─────────────────────────┘

把这三部分连起来:

1 10000010 00001000000000000000000

所以它的含义不是“尾数为负”,而是:

(-1)^1 × 1.00001₂ × 2^3
= -8.25

2. 阶码字段 e 和阶码真值 E 不是一个东西

这是理解偏置值最重要的一步。

阶码真值 E:真正参与 2^E 的指数
阶码字段 e:机器中存的 8 位无符号数

二者关系是:

e = E + 127
E = e - 127

也就是说,机器里不直接存 E = 3,而是存:

3 + 127 = 130

可以画成这样:

          阶码真值 E                    阶码字段 e
      ┌─────────────┐              ┌─────────────┐
      │     +3      │  加偏置127    │     130     │
      └─────────────┘ ───────────→ └─────────────┘
                                        │
                                        ▼
                                   10000010₂

还原时反过来:

          阶码字段 e                    阶码真值 E
      ┌─────────────┐              ┌─────────────┐
      │     130     │  减偏置127    │     +3      │
      └─────────────┘ ───────────→ └─────────────┘

所以 127 的作用不是改变数值本身,而是让“可能为负的指数”可以存到一个“无符号阶码字段”里。

3. 用表格看:为什么 e 当无符号数时不会变成 -128

你提到的“127 + 1 = -128”,这是8 位补码解释下的现象。

但 IEEE 754 的阶码字段不是按补码解释,而是按无符号数解释。同样的 8 位二进制,在不同解释规则下含义不同。

位模式按 8 位无符号数解释按 8 位补码解释IEEE 754 单精度阶码含义
01111111127127E = 127 - 127 = 0
10000000128-128E = 128 - 127 = +1
10000001129-127E = 129 - 127 = +2
11111110254-2E = 254 - 127 = +127
11111111255-1特殊值:无穷大或 NaN

所以在 IEEE 754 阶码字段里:

10000000₂ 不是 -128
10000000₂ 是无符号数 128
阶码真值 E = 128 - 127 = 1

也就是说:

“127 + 1 = -128”只在补码语境中成立;
IEEE 754 阶码字段不按补码解释,所以不会发生这个跳变。

这一点是最关键的。

4. 用一条数轴看偏置映射

单精度阶码字段有 8 位。机器中阶码字段 e 按无符号数看,本来范围是:

0 ~ 255

但 IEEE 754 中:

e = 0     保留给 0 和非规格化数
e = 255   保留给无穷大和 NaN

规格化数真正使用的是:

e = 1 ~ 254

对应阶码真值:

E = e - 127

映射关系如下:

阶码字段 e:  0      1      2      ...    126    127    128    ...    254     255
             │      │      │              │      │      │             │       │
             │      ▼      ▼              ▼      ▼      ▼             ▼       │
阶码真值 E: 特殊   -126   -125    ...    -1      0     +1     ...    +127    特殊

因此:

e = 127 时,E = 0
e = 128 时,E = +1
e = 126 时,E = -1

这就像把指数数轴整体向右平移了 127,使得负指数也能用非负的字段值表示。

这张图的核心不是曲线形状,而是对应关系:e 每增加 1,E 也增加 1,只是整体差了一个 127。

5. 127 到底怎么来的?不是从补码推出来的

127 不是由补码定义推出来的。它来自 IEEE 754 对 k 位阶码字段的偏置规定:

Bias = 2^(k-1) - 1

单精度 float 的阶码字段是 8 位,所以:

Bias = 2^(8-1) - 1
     = 2^7 - 1
     = 128 - 1
     = 127

双精度 double 的阶码字段是 11 位,所以:

Bias = 2^(11-1) - 1
     = 2^10 - 1
     = 1023

它和补码的联系只能说是:二者都在解决“如何用固定位数表示正负数”的问题,但方法不同。

补码:把最高位解释成负权值
偏置表示:先给真值加一个固定偏移量,再按无符号数存

两者不是同一个编码体系。

6. 8 位补码和 8 位偏置阶码的本质区别

8 位补码

8 位补码的权值可以理解为:

b7 b6 b5 b4 b3 b2 b1 b0

数值 = -b7×128 + b6×64 + b5×32 + ... + b0×1

所以:

01111111₂ = 127
10000000₂ = -128

因为最高位 b7 = 1 时,它代表的是 -128 的负权值。

IEEE 754 阶码字段

IEEE 754 的阶码字段先按无符号数读:

b7 b6 b5 b4 b3 b2 b1 b0

e = b7×128 + b6×64 + b5×32 + ... + b0×1

然后再计算:

E = e - 127

所以:

01111111₂ = 无符号 127 → E = 127 - 127 = 0
10000000₂ = 无符号 128 → E = 128 - 127 = 1

这就是二者最根本的区别。

可以把它整理成一句话:

补码是“位模式直接带符号解释”;
阶码偏置是“位模式先按无符号数解释,再整体减偏置”。

7. 为什么 IEEE 754 不直接用补码表示阶码?

考研中不必深入硬件设计,但需要知道偏置表示有一个直接好处:阶码字段越大,阶码真值也越大,比较大小更方便。

例如:

E = -1  → e = 126 → 01111110
E =  0  → e = 127 → 01111111
E = +1  → e = 128 → 10000000

如果把 e 当无符号数看:

126 < 127 < 128

刚好对应:

-1 < 0 < +1

这种顺序关系很自然。

如果阶码用补码,负阶码的位模式会排到很大的无符号值区域里,例如:

-1 的 8 位补码 = 11111111

这样在硬件比较、排序、规格化处理时不如偏置阶码方便。

所以 IEEE 754 选择偏置阶码,而不是补码阶码。

8. 为什么偏置是 127,而不是 128?

从“普通移码”直觉看,8 位如果想平移,似乎可以用 128。但 IEEE 754 用的是:

2^(k-1) - 1

对于 8 位就是 127。

一个重要原因是 IEEE 754 要把阶码字段全 0 和全 1 留作特殊用途:

00000000:0 和非规格化数
11111111:无穷大和 NaN

所以规格化数只用:

00000001 ~ 11111110

即:

e = 1 ~ 254

如果 Bias = 127,则规格化阶码真值范围是:

e = 1   → E = 1 - 127 = -126
e = 254 → E = 254 - 127 = +127

所以单精度规格化阶码范围为:

-126 ~ +127

这个范围略微偏向正数一侧,并且配合特殊阶码字段完成 0、非规格化数、无穷大、NaN 的表示。

因此对于考研来说,不要尝试从补码的 -128 ~ +127 去强行推出 IEEE 754 的偏置。应该记成:

IEEE 754 k 位阶码偏置 = 2^(k-1) - 1
float:k = 8,Bias = 127
double:k = 11,Bias = 1023

9. 把 float 的三个字段放到一张“职责图”里

以规格化 float 为例:

┌────────────────────────────────────────────────────────────┐
│                        32 位 float                         │
├───────────┬────────────────────┬───────────────────────────┤
│ 符号位 S  │      阶码字段 e      │         尾数字段 M          │
│   1位     │        8位          │           23位             │
├───────────┼────────────────────┼───────────────────────────┤
│ 决定正负  │ 先按无符号数读取 e   │ 保存 1.M 中小数点后的部分   │
│ 0 正 1 负 │ 再 E = e - 127      │ 最高位 1 隐藏不存           │
└───────────┴────────────────────┴───────────────────────────┘

最终真值:

(-1)^S × 1.M × 2^E

对于 -8.25

-8.25 = -1.00001₂ × 2^3

┌───────────────┬──────────────────────┬──────────────────────┐
│ 符号位 S       │ 阶码字段 e             │ 尾数字段 M             │
├───────────────┼──────────────────────┼──────────────────────┤
│ 1             │ 3 + 127 = 130         │ 00001 后补 0           │
│ 表示负数       │ 10000010₂             │ 表示有效数字 .00001     │
└───────────────┴──────────────────────┴──────────────────────┘

10. 最后用一句考研式判断法收束

看到 IEEE 754 单精度题,不要把阶码字段当补码看。固定流程是:

第一步:把数写成 ±1.xxxx₂ × 2^E
第二步:符号位 S 看正负
第三步:阶码字段 e = E + 127
第四步:尾数字段 M 只存 xxxx,不存隐藏的最高位 1

对于你说的 127 + 1 = -128,应当这样改正:

如果是 8 位补码,01111111 + 1 = 10000000,解释为 -128;
如果是 IEEE 754 阶码字段,10000000 按无符号数解释为 128,再减 127,得到阶码真值 +1。

所以二者的位模式可能一样,但解释规则完全不同。IEEE 754 的阶码字段是“无符号数 + 偏置解释”,不是补码解释。


某计算机存储器按字节编址,采用小端方式存放数据。假定编译器规定 int型和 short 型长度分别为 32位和 16位,并且数据按边界对齐存储。某 C语言程序段如下:
struct{
int a;
char b:;
short C;
}record;
record.a= 273;
若record 变量的首地址为0xC008,地址0xC008中的内容及record.c的地址分别为()。A.0x00,0xC00DB0x00,0xC00EC0x11,0xC00DD0x11,0xC00E ,你能跟我讲一下它这个据按边界对齐方式储存,究竟是按哪一种边界来对齐呢?为什么不是a给4B,成员b给四个B,成员c也有四个B呢?

本题答案应选 D:0x11,0xC00E

这里的结构体可以按下面理解:

struct {
    int   a;   // 4B
    char  b;   // 1B
    short c;   // 2B
} record;

题目说:

int 型 32 位,short 型 16 位,数据按边界对齐存储。

在 408 这类题中,“按边界对齐”通常不是说所有成员都按 4B 分配空间,而是说:

每个成员变量的起始地址,要按它自身类型的长度边界对齐。

也就是:

类型占用空间常见对齐要求
char1B地址任意,1 字节对齐
short2B起始地址是 2 的整数倍
int4B起始地址是 4 的整数倍

所以不是 a 占 4B、b 也占 4B、c 也占 4B。
对齐不是“每个成员都补成 4B”,而是“成员起始地址满足自己的边界要求”。


record 的首地址是:

record 首地址 = 0xC008

因为 aint,占 4B,而且 0xC008 正好是 4 的整数倍,所以 a 从 0xC008 开始存:

a 占用:
0xC008
0xC009
0xC00A
0xC00B

题目给:

record.a = 273

273 转成十六进制是:

273 = 0x00000111

由于采用小端方式存放,低字节放低地址,所以:

0x00000111 拆成 4 个字节:

高地址方向
00 00 01 11

小端存储:
低地址存低字节

所以内存中是:

地址        内容
0xC008     0x11
0xC009     0x01
0xC00A     0x00
0xC00B     0x00

因此地址 0xC008 中的内容是:

0x11

接着看 b

bchar,占 1B。a 已经占用了 0xC008 到 0xC00B,所以下一个地址是:

0xC00C

char 只要求 1 字节对齐,任何地址都可以,所以 b 直接放在:

b 地址 = 0xC00C

占用:

0xC00C

然后看 c

cshort,占 2B,要求起始地址是 2 的整数倍。

b 占完以后,下一个可用地址是:

0xC00D

但是:

0xC00D 是奇地址

它不是 2 的整数倍,所以不能作为 short c 的起始地址。

因此编译器会在 b 后面填充 1 个字节的空洞,也就是 padding:

0xC00D:填充字节,不属于任何成员

然后 c 从下一个偶地址开始:

c 地址 = 0xC00E

c 占用:

0xC00E
0xC00F

所以 record.c 的地址是:

0xC00E

整体布局如下:

地址        内容/成员
0xC008     a 的第 1 字节:0x11
0xC009     a 的第 2 字节:0x01
0xC00A     a 的第 3 字节:0x00
0xC00B     a 的第 4 字节:0x00
0xC00C     b,占 1B
0xC00D     padding,对齐填充字节
0xC00E     c 的第 1 字节
0xC00F     c 的第 2 字节

所以:

地址 0xC008 中的内容 = 0x11
record.c 的地址 = 0xC00E

答案为:

D. 0x11, 0xC00E

这类题最容易错在把“边界对齐”理解成“每个成员都按最大成员长度分配空间”。这是不对的。

更准确的考研做题规则是:

成员按声明顺序依次存放;
每个成员的起始地址必须是自身对齐边界的整数倍;
不满足时,在前一个成员后面填充若干空字节;
结构体整体大小通常还要补齐到最大成员对齐边界的整数倍。

本题最大成员是 int,对齐边界是 4B。最后结构体实际占用到 0xC00F,刚好总大小是 8B,已经是 4 的整数倍,所以尾部不需要再额外填充。

先给答案:

31 A
32 A
33 D
34 A
35 A
36 A
37 D
38 A

下面逐题讲。

31 题

题目给出的 IEEE 754 单精度浮点数机器数为:

C640 0000H

先转成二进制看结构。IEEE 754 单精度格式为:

1 位符号位 + 8 位阶码 + 23 位尾数

C6400000H 的最高位是 1,所以符号为负。

继续拆:

C6400000H
= 1100 0110 0100 0000 0000 0000 0000 0000

拆成:

符号位 S = 1

阶码 E = 10001100₂ = 140

尾数字段 M = 10000000000000000000000

单精度浮点数的阶码偏置值是 127,所以真实阶码为:

e = 140 - 127 = 13

规格化数的尾数前面隐藏一个 1,所以有效尾数是:

1.1₂ = 1.5

因此该数为:

-1.5 × 2¹³

所以 31 题选 A。

这类题的固定做法就是:看最高位定正负,看 8 位阶码减 127,看尾数字段前面补隐藏的 1。

关于31题你给出的解答中10001100₂,1.1₂ = 1.5这么后面的这个下标z是什么意思以及你给我的规格化尾数,前面隐藏了一个一所以有效尾数1.1₂ = 1.5,这个是怎么推导出来的?能不能稍微讲的详细一点?

这里的下标不是 z,而是 ,表示“二进制”。

比如:

10001100₂

读作“二进制数 10001100”。

如果写成十进制,就是:

10001100₂ = 1×2⁷ + 0×2⁶ + 0×2⁵ + 0×2⁴ + 1×2³ + 1×2² + 0×2¹ + 0×2⁰
          = 128 + 8 + 4
          = 140

所以 10001100₂ 表示二进制的 10001100,它的十进制值是 140


回到 31 题的浮点数:

C640 0000H

先转成二进制:

C6400000H
= 1100 0110 0100 0000 0000 0000 0000 0000

IEEE 754 单精度格式是:

1 位符号位 + 8 位阶码 + 23 位尾数

所以拆成:

1 | 10001100 | 10000000000000000000000

也就是:

符号位 S = 1
阶码字段 E = 10001100₂
尾数字段 F = 10000000000000000000000

符号位 S = 1,说明这个数是负数。

阶码字段:

10001100₂ = 140

IEEE 754 单精度的阶码偏置值是 127,所以真实阶码为:

e = 140 - 127 = 13

关键是尾数部分。

IEEE 754 规格化数的尾数不是直接写成 0.100000...,而是默认前面隐藏了一个 1。也就是说,尾数字段只保存小数点后面的部分。

题目中的尾数字段是:

10000000000000000000000

它表示:

小数点后面是 .10000000000000000000000

因为规格化数前面默认隐藏一个 1,所以真正参与计算的有效尾数是:

1.10000000000000000000000₂

后面的 0 可以省略,所以写成:

1.1₂

这就是我之前写的:

有效尾数 = 1.1₂

再看 1.1₂ = 1.5 是怎么来的。

二进制小数的位权和十进制类似,只不过底数是 2。

对于二进制数:

1.1₂

小数点左边的 1 表示:

1 × 2⁰ = 1

小数点右边第一位的 1 表示:

1 × 2⁻¹ = 1/2 = 0.5

所以:

1.1₂ = 1×2⁰ + 1×2⁻¹
     = 1 + 0.5
     = 1.5

如果是:

1.01₂

那么它就是:

1×2⁰ + 0×2⁻¹ + 1×2⁻²
= 1 + 0 + 0.25
= 1.25

如果是:

1.101₂

那么它就是:

1×2⁰ + 1×2⁻¹ + 0×2⁻² + 1×2⁻³
= 1 + 0.5 + 0 + 0.125
= 1.625

所以二进制小数点右边第一位是 2⁻¹,第二位是 2⁻²,第三位是 2⁻³,以此类推。


把 31 题完整连起来就是:

C6400000H
= 1 | 10001100 | 10000000000000000000000

符号位 S = 1,所以是负数

阶码字段 E = 10001100₂ = 140
真实阶码 e = 140 - 127 = 13

尾数字段 F = 100000...
规格化数隐藏最高位 1
所以有效尾数 = 1.100000...₂ = 1.1₂ = 1.5

因此这个浮点数表示:

-1.5 × 2¹³

也就是:

-1.5 × 8192 = -12288

不过本题选项写的是科学计数形式,所以答案保留为:

-1.5 × 2¹³

这类 IEEE 754 单精度题可以固定记成一句话:

规格化浮点数 = (-1)^符号位 × 1.尾数字段 × 2^(阶码字段值 - 127)

其中 1.尾数字段 里的这个 1 是隐藏位,不出现在机器数里,但计算时必须补回来。


32 题

题目给:

f1 = CC90 0000H
f2 = B0C0 0000H

它们都是 IEEE 754 单精度浮点数。

先看符号位。十六进制最高位如果在 8F 之间,最高二进制位就是 1,说明是负数。

CC90 0000H:最高十六进制位 C,符号位为 1,负数
B0C0 0000H:最高十六进制位 B,符号位为 1,负数

所以 x 和 y 符号相同,都是负数。

再比较大小。

CC90 0000H 拆出来大致是:

符号位 = 1
阶码 = 153
真实阶码 = 153 - 127 = 26
尾数 = 1.125

x = -1.125 × 2²⁶

B0C0 0000H 拆出来大致是:

符号位 = 1
阶码 = 97
真实阶码 = 97 - 127 = -30
尾数 = 1.5

y = -1.5 × 2⁻³⁰

一个是很大的负数,一个是非常接近 0 的负数:

x = -1.125 × 2²⁶
y = -1.5 × 2⁻³⁰

所以:

x < y

并且二者符号相同。

所以 32 题选 A。

这题的陷阱是:不能只看阶码大就说数值大。因为它们都是负数,绝对值越大,实际值越小。


33 题

题目考浮点数加减运算过程中的溢出判断。

四个说法分别是:

I. 对阶操作不会引起阶码上溢或下溢

II. 右规和尾数舍入都可能引起阶码上溢

III. 左规时可能引起阶码下溢

IV. 尾数溢出时结果不一定溢出

逐个判断。

I 正确

浮点数加减前要对阶,一般是“小阶向大阶看齐”,也就是把阶码小的数的尾数右移,使它的阶码变成较大的那个阶码。

例如:

1.001 × 2⁵
0.101 × 2²

对阶时,把第二个数改成:

0.000101 × 2⁵

这个过程只是让小阶数的阶码变成已有的较大阶码,不会产生新的超过范围的阶码。

所以对阶本身不会引起阶码上溢或下溢。它可能造成尾数低位丢失,也就是“大数吃小数”,但这不是阶码溢出。

I 正确。

II 正确

右规通常发生在尾数相加后产生进位的时候。

例如:

1.101 + 1.011 = 11.000

结果不是规格化形式,需要右移一位,阶码加 1:

11.000 × 2ᵉ
= 1.1000 × 2ᵉ⁺¹

如果原来的阶码已经很大,再加 1 就可能阶码上溢。

尾数舍入也可能导致尾数进位。例如舍入后:

1.111...111  舍入后变成 10.000...000

这时也需要右规,阶码加 1,也可能导致阶码上溢。

所以 II 正确。

III 正确

左规通常发生在两个接近的数相减后,前导有效位变成 0,需要尾数左移、阶码减小。

例如:

1.0001 × 2⁻¹²⁶ - 1.0000 × 2⁻¹²⁶
= 0.0001 × 2⁻¹²⁶

为了规格化,需要左移尾数,同时阶码不断减小。

如果阶码已经接近最小值,再继续减小,就可能下溢。

所以 III 正确。

IV 正确

尾数溢出不代表最终浮点数一定溢出。

尾数相加后如果变成:

10.xxxx

可以右规成:

1.0xxxx × 2ᵉ⁺¹

只要阶码加 1 后仍然在可表示范围内,最终结果就不溢出。

所以 IV 正确。

四个说法都正确。

所以 33 题选 D。

这题要记住一句话:尾数溢出不等于浮点数溢出;真正决定结果是否溢出的是规格化、舍入之后的阶码是否越界。


34 题

题目条件:

字长 32 位
按字节编址
小端方式
double 型变量机器数为 1122 3344 5566 7788H
从地址 0000 8040H 开始连续存放
问地址 0000 8046H 中存放什么

小端方式的核心规则是:

低有效字节放低地址,高有效字节放高地址。

机器数:

1122 3344 5566 7788H

从高到低的字节是:

11 22 33 44 55 66 77 88

但是小端存储时要反过来放:

地址 0000 8040H:88H
地址 0000 8041H:77H
地址 0000 8042H:66H
地址 0000 8043H:55H
地址 0000 8044H:44H
地址 0000 8045H:33H
地址 0000 8046H:22H
地址 0000 8047H:11H

所以地址 0000 8046H 中存放的是:

22H

所以 34 题选 A。

这题不要被“字长 32 位”干扰。题目问的是按字节编址下某个字节地址中的内容,关键看小端字节序。


34题里面不选d选a的主要原因是因为一个字节相当于有两个字,对吧?我们计算机里面中储存单元每增一位等于一个字节对吧?

关键原因就是:题目说“按字节编址”,所以地址每增加 1,移动的是 1 个字节(8 bit),不是移动 1 个字,也不是移动 1 个十六进制数。

但这里有一个说法需要纠正一下:

不是“一个字节相当于有两个字”
而是“一个字节相当于两个十六进制位”

比如:

22H = 0010 0010₂

这里 22H 是两个十六进制位,每个十六进制位对应 4 bit,所以:

2 个十六进制位 = 8 bit = 1 字节

所以应该这样理解:

1 个十六进制位 = 4 bit
2 个十六进制位 = 8 bit = 1 Byte

而“字”这个概念和机器字长有关。题目说这台机器字长是 32 位,那么:

1 字 = 32 bit = 4 字节

所以在这题中:

1 字节 = 8 bit = 2 个十六进制位
1 字 = 32 bit = 4 字节 = 8 个十六进制位

不要把“两个十六进制位”和“两个字”混在一起。


34 题给的是:

double 型变量的机器数为:1122 3344 5566 7788H
从地址 0000 8040H 开始连续存放
按字节编址,小端方式
问地址 0000 8046H 中存放的内容

1122 3344 5566 7788H 这个 64 位数据,按字节拆开是:

11H 22H 33H 44H 55H 66H 77H 88H

每一个 xxH 正好是 1 个字节。

由于是小端方式,低有效字节放低地址。这个数的低有效字节是最右边的 88H,高有效字节是最左边的 11H

所以内存中从低地址到高地址依次放:

地址 0000 8040H:88H
地址 0000 8041H:77H
地址 0000 8042H:66H
地址 0000 8043H:55H
地址 0000 8044H:44H
地址 0000 8045H:33H
地址 0000 8046H:22H
地址 0000 8047H:11H

因此地址 0000 8046H 中的内容是:

22H

所以选 A。


这题不选 D 的原因不是因为 double 被分成两个 32 位字来存,而是因为题目问的是“某个字节地址里的内容”。在按字节编址的机器中,每个地址对应 1 字节,所以必须一个字节一个字节地排。

也就是说,解这种题时要抓住三句话:

按字节编址:地址 +1 表示移动 1 字节

一个字节:8 bit = 2 个十六进制位

小端方式:低有效字节放低地址

34 题中 0000 8046H 比起起始地址 0000 8040H 偏移了:

8046H - 8040H = 6

也就是第 7 个字节。小端存放顺序是:

88 77 66 55 44 33 22 11

第 7 个字节正好是 22H

35 题

题目问 IEEE 754 单精度浮点格式中,最小的规格化正数是多少。

单精度格式:

符号位:1 位
阶码:8 位
尾数:23 位
阶码偏置值:127

规格化数要求阶码字段不能全 0,也不能全 1。

所以规格化正数中,最小阶码字段是:

E = 00000001₂ = 1

真实阶码为:

e = 1 - 127 = -126

尾数要最小,尾数字段全 0。规格化数有隐藏的最高位 1,所以有效尾数是:

1.0

因此最小规格化正数是:

1.0 × 2⁻¹²⁶

所以 35 题选 A。

注意区分:

最小规格化正数:1.0 × 2⁻¹²⁶
最小非规格化正数:1.0 × 2⁻¹⁴⁹

本题问的是“规格化正数”,不是“最小正数”。


36 题

题目给:

int i = 0;

对应指令机器代码为:

C7 45 FC 00 00 00 00

问:

int i = -64;

对应指令机器代码是什么。

这其实是一个小端立即数存储问题。

前面的:

C7 45 FC

可以理解为指令操作码和寻址相关内容,它们不变。

后面的:

00 00 00 00

是立即数 0 的 32 位表示。

现在要把立即数换成 -64

32 位补码中:

64 = 0000 0040H
-64 = FFFF FFC0H

因为是小端方式,FFFF FFC0H 在机器代码中按字节存放为:

C0 FF FF FF

所以完整指令是:

C7 45 FC C0 FF FF FF

所以 36 题选 A。

这题的易错点是把整个指令全部倒过来。小端影响的是多字节数据的编码顺序,不是把整条指令从头到尾反转。


37 题

结构体为:

struct record{
    short x1;
    int   x2;
} a;

条件:

32 位计算机
按字节编址
小端方式
按边界对齐
a 的首地址为 2020 FE00H
x2 的机器数为 1234 0000H
问其中 34H 所在存储单元地址

先分析结构体布局。

short 占 2 字节,通常按 2 字节边界对齐。

int 占 4 字节,通常按 4 字节边界对齐。

结构体首地址:

2020 FE00H

成员 x1 是 short,占 2 字节:

2020 FE00H:x1 的第 1 字节
2020 FE01H:x1 的第 2 字节

接下来如果直接放 x2,地址会是:

2020 FE02H

但是 int x2 要按 4 字节边界对齐,地址必须是 4 的倍数。FE02H 不是 4 字节边界,所以要填充 2 个空字节:

2020 FE02H:填充
2020 FE03H:填充

于是 x2 从:

2020 FE04H

开始存放。

现在 x2 的机器数是:

1234 0000H

小端方式存储,低有效字节放低地址:

2020 FE04H:00H
2020 FE05H:00H
2020 FE06H:34H
2020 FE07H:12H

所以 34H 所在地址是:

2020 FE06H

所以 37 题选 D。

这题的核心不是单纯小端,而是先做结构体边界对齐,再做小端存储。顺序不能反。


38 题

题目条件:

有符号整数用补码表示
float 用 IEEE 754 标准表示
变量 x 的类型只能是 int 或 float
x 的机器数为 C800 0000H
问 x 的值可能是什么

也就是说,同一串二进制位可能按 int 解释,也可能按 float 解释,需要分别看。

按 int 解释

机器数:

C800 0000H

最高位为 1,所以作为 32 位补码整数时是负数。

求真值:

C800 0000H 对应的无符号值 = C8000000H

负数补码真值 = C8000000H - 100000000H
             = -38000000H

而:

38000000H = 56 × 2²⁴ = 7 × 2²⁷

所以按 int 解释时:

x = -7 × 2²⁷

这正好对应选项 A。

按 float 解释

C8000000H 按 IEEE 754 单精度浮点数拆:

符号位 = 1,负数
阶码字段 = 10010000₂ = 144
真实阶码 = 144 - 127 = 17
尾数字段全 0,所以有效尾数 = 1.0

所以按 float 解释时:

x = -1.0 × 2¹⁷ = -2¹⁷

选项里没有 -2¹⁷

因此机器数 C8000000H 可能表示的值只能是:

-7 × 2²⁷

所以 38 题选 A。

这题的陷阱是:题目说 x 的类型可能是 int 或 float,所以不能只按一种类型解释。要分别按补码整数和 IEEE 754 浮点数都解释一遍,再看哪个选项匹配。

答案是:

40:A
41:A
42:A
43:C
44:D
45:C

下面逐题讲。

40 题:-0.4375 的 IEEE 754 单精度表示

IEEE 754 单精度格式是:

1 位符号位 + 8 位阶码 + 23 位尾数

规格化数的真值形式是:

(-1)^s × 1.M × 2^(E - 127)

先把 0.4375 转成二进制:

0.4375 = 7 / 16 = 0.0111₂

规格化:

0.0111₂ = 1.11₂ × 2^-2

因为是负数,所以符号位:

s = 1

阶码真值是 -2,单精度偏置值是 127,所以阶码字段为:

E = -2 + 127 = 125 = 01111101₂

尾数部分只保存隐藏的 1 后面的部分,也就是:

1.11₂ → 尾数字段为 11000000000000000000000

所以整体为:

1 | 01111101 | 11000000000000000000000

写成 16 进制:

1011 1110 1110 0000 0000 0000 0000 0000
= BEE0 0000H

所以第 40 题选 A。

41 题:short 型变量 x = -8190 的机器数

short 型一般按 16 位补码表示。

先求正数 8190 的 16 进制:

8190 = 8192 - 2 = 2000H - 2 = 1FFEH

所以:

+8190 = 1FFEH

负数补码可以用:

2^16 - 8190

也就是:

10000H - 1FFEH = E002H

所以:

-8190 的 16 位补码 = E002H

第 41 题选 A。

这一类题要记住:short 是 16 位有符号整数,负数机器数默认考补码。

42 题:机器数 8020 0000H 表示的 float 值

先写成二进制:

8020 0000H
= 1000 0000 0010 0000 0000 0000 0000 0000₂

按 IEEE 754 单精度拆:

符号位 s = 1
阶码字段 E = 00000000
尾数字段 M = 01000000000000000000000

这里非常关键:阶码字段全 0,尾数不全 0,所以它不是规格化数,而是非规格化数。

非规格化数没有隐藏的 1,它的形式是:

(-1)^s × 0.M × 2^-126

尾数字段是:

0.010000...₂ = 2^-2

所以真值为:

- 0.01₂ × 2^-126
= -2^-2 × 2^-126
= -2^-128

所以第 42 题选 A。

这里容易错在把它当成规格化数。如果阶码全 0,一定不能再默认前面隐藏一个 1。只有规格化数才有隐藏的 1。

44 题:4730 0000H 表示的 float 值

机器数为:

4730 0000H
= 0100 0111 0011 0000 0000 0000 0000 0000₂

拆成 IEEE 754 单精度格式:

符号位 s = 0
阶码字段 E = 10001110₂
尾数字段 M = 01100000000000000000000

阶码字段:

10001110₂ = 142

阶码真值:

142 - 127 = 15

尾数部分:

1.M = 1.011₂

而:

1.011₂ = 1 + 0.25 + 0.125 = 1.375

所以该浮点数的值为:

1.375 × 2^15

第 44 题选 D。

45 题:结构体数组、小端方式和边界对齐

结构体为:

struct record {
    int id;
    char name[10];
    int salary;
} employee[200];

已知:

employee 首地址 = 0000 A0B0H
employee[1].id 的机器数 = 1234 5678H

先看结构体内部布局。

在 32 位机器中:

int  占 4B,通常按 4 字节边界对齐
char 占 1B,按 1 字节对齐

结构体布局如下:

id       占 4B,偏移 0 ~ 3
name[10] 占 10B,偏移 4 ~ 13

此时下一个可用偏移是 14。但 salary 是 int,需要按 4 字节边界对齐,所以要补齐到偏移 16。

偏移 14 ~ 15:填充字节
salary:偏移 16 ~ 19

所以一个 struct record 的大小是:

20B = 14H

因此:

employee[0] 首地址 = 0000 A0B0H
employee[1] 首地址 = 0000 A0B0H + 14H = 0000 A0C4H

而 id 是结构体第一个成员,所以:

employee[1].id 的起始地址 = 0000 A0C4H

题目说 employee[1].id 的机器数是:

1234 5678H

采用小端方式存放数据,即低位字节放低地址,高位字节放高地址。

所以存储情况是:

地址          内容
0000 A0C4H    78H
0000 A0C5H    56H
0000 A0C6H    34H
0000 A0C7H    12H

因此 56H 所在存储单元地址是:

0000 A0C5H

第 45 题选 C。

这一题的关键顺序是:先算结构体大小,再算数组第 2 个元素首地址,最后按小端方式找字节位置。

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇