0
点赞
收藏
分享

微信扫一扫

汇编 —— 尾数、负数加法、标准浮点数


大小尾数

小尾数:低位字节存储在低内存位置或寄存器的低位地址,高位字节存储在高内存位置或寄存器的高位地址。
大尾数:和小尾数的存储顺序是相反的,低位字节存储在高位地址。

intel采用的是小尾数存储格式:
检测:

.section .data
value:
.int 0x127
.section .text
.global main
main:
nop
movl $0x127,%ebx
int $0x80

查看内存情况:

(gdb) x/x &value
0x8049664 <value>: 0x27
(gdb) x/4x &value
0x8049664 <value>: 0x27 0x01 0x00 0x00
(gdb) p/x $ebx
$1 = 0x127
(gdb) p/x $bx
$2 = 0x127
(gdb) p/x $bl
$3 = 0x27
(gdb) p/x $bh
$4 = 0x1

负数的数学计算

在计算机中,四则运算以加法为基础。理解负数在计算机中以补码表示,负数和非负数的加法计算,负数和负数的加法计算很重要。

使用gdb简单查看负数的加法操作:
汇编:

.section .text
.global main
main:
nop
movl $24,%edx
movl $-35,%ecx
addl %edx,%ecx
movl $-24,%edx
addl %edx,%ecx
int $0x80

gdb调试:

Temporary breakpoint 1, main () at negtive.s:4
4 nop
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.192.el6.i686
(gdb) n
5 movl $24,%edx
(gdb) n
6 movl $-35,%ecx
(gdb) n
7 addl %edx,%ecx
(gdb) p/t $edx
$1 = 11000
(gdb) p/t $ecx
$2 = 11111111111111111111111111011101
(gdb) n
8 movl $-24,%edx
(gdb) p/t $ecx
$3 = 11111111111111111111111111110101
(gdb) p $ecx
$4 = -11
(gdb) n
9 addl %edx,%ecx
(gdb) p $edx
$5 = -24
(gdb) p/t $edx
$6 = 11111111111111111111111111101000
(gdb) n
10 int $0x80
(gdb) p $ecx
$7 = -35
(gdb) p/t $ecx
$8 = 11111111111111111111111111011101

整个过程简单地说是这样的:

00000000000000000000000000011000 + 
11111111111111111111111111011101 =
11111111111111111111111111110101
即是:24 + -35 = -11
11111111111111111111111111101000 +
11111111111111111111111111110101

标准浮点数据

标准浮点数据类型,IEEE标准754顶的浮点数

32位单精度浮点数

汇编 —— 尾数、负数加法、标准浮点数_存储


因为假设有效数字的整数值是1,并且不在有效数字值中使用它,所以精度就变成了23+1=24。

指数使用8位,那么它的范围应该是-128~127.

单精度浮点数最大绝对值是1.9˙×2127≈1.9˙×1.701×1038≈3.4×1038

64位双精度浮点数

汇编 —— 尾数、负数加法、标准浮点数_存储_02


指数的位数是11,2^11=2048,那么指数的范围是-1024~1023.

双精度的最大绝对值估计为:1.9˙∗21023≈1.79×10308

拓展双精度

IA-32平台,拓展双精度格式使用在80位FPU寄存器中。

汇编 —— 尾数、负数加法、标准浮点数_存储_03


指数位是15,2^15=32768,那么指数的范围是-16384~16383,拓展双精度的最大绝对值是:1.9˙×216383≈1.18×104932

由此我们得到下面这张表:

数据类型

长度

有效数字位

指数位

精度范围

单精度

32

23

8

1.18×10−38∼3.4∗1038

双精度

64

53

11

2.23×10−308∼1.79×10308

拓展双精度

80

64

15

3.37×10−4932∼1.18×104932

最小的精度值我没能想通它是怎么算出来的。望交流。

浮点数转化成二进制

(例子讲解12.23转化成01000001010000111010111000010100):
1. 提取小数部分0.23,乘以2,如果整数部分是1,那么十分位是1,否则是0 0.23×2=0.46decimals:0.0;
再取小数部分乘以2,判断是否在百分位上写1 0.46×2=0.92decimals:0.00;
继续刚才的步骤,
0.92×2=1.84decimals:0.001
0.84×2=1.68decimals:0.0011
0.68×2=1.36decimals:0.00111
0.36×2=0.72decimals:0.001110
0.72×2=1.44decimals:0.0011101
0.44×2=0.88decimals:0.00111010
0.88×2=1.76decimals:0.001110101
0.76×2=1.52decimals:0.0011101011
0.52×2=1.04decimals:0.00111010111
0.04×2=0.08decimals:0.001110101110
0.08×2=0.16decimals:0.0011101011100
0.16×2=0.32decimals:0.00111010111000
0.32×2=0.64decimals:0.001110101110000
0.64×2=1.28decimals:0.0011101011100001
0.28×2=0.56decimals:0.00111010111000010
0.56×2=1.12decimals:0.001110101110000101
0.12×2=0.24decimals:0.0011101011100001010
0.24×2=0.48decimals:0.00111010111000010100
(停,单精度已经装不下了)
2. 转化整数部分成二进制形式 11002,和小数部分写在一起: 1100.001110101110000101002
如果是左移,第30位填1,左移位数n减去1再换成二进制形式填入指数位;若是右移或者n=0,第30位填0,第29位到第23位放n的二进制数,然后取反。
3. 转换成有效数字,左移3位,1100.001110101110000101002⟶1.100001110101110000101002
4. 将最左边的1去掉小数部分是23位,正是单精度的有效数字部分。
5. 因为是左移,那么第30位写1,左移位数3减去1等于2,填到指数位,​​​10000010​​​就是指数位的构成。
6. 因为12.23是正数,所以我们在第31位填0. 得到最终的结果:​​​01000001010000111010111000010100​

当然这是有精度缺失的,gdb调试:
float.s:

.section .data
value1:
.float 12.23
.section .text
.global main
main:
nop
int $0x80

gdb查看内存:

(gdb) x/t &value1
0x8049654 <value1>: 01000001010000111010111000010100
(gdb) x/f &value1
0x8049654 <value1>: 12.2299995

在双精度存储的条件下,因为有效位数和指数位都明显增加,所以精度变高。

(gdb) x/gf &value1
0x8049654 <value1>: 12.23
(gdb) x/t &value1
0x8049654 <value1>: 0100000000101000011101011100001010001111010111000010100011110110

对于小数点需要右移的情况:
value = 0.23
1. 和左移的情况类似:
0.23×2=0.46decimals:0.0
0.46×2=0.92decimals:0.00
0.92×2=1.84decimals:0.001
0.84×2=1.68decimals:0.0011
0.68×2=1.36decimals:0.00111
0.36×2=0.72decimals:0.001110
0.72×2=1.44decimals:0.0011101
0.44×2=0.88decimals:0.00111010
0.88×2=1.76decimals:0.001110101
0.76×2=1.52decimals:0.0011101011
0.52×2=1.04decimals:0.00111010111
0.04×2=0.08decimals:0.001110101110
0.08×2=0.16decimals:0.0011101011100
0.16×2=0.32decimals:0.00111010111000
0.32×2=0.64decimals:0.001110101110000
0.64×2=1.28decimals:0.0011101011100001
0.28×2=0.56decimals:0.00111010111000010
0.56×2=1.12decimals:0.001110101110000101
0.12×2=0.24decimals:0.0011101011100001010
0.24×2=0.48decimals:0.00111010111000010100
more:
0.48×2=0.96decimals:0.001110101110000101000
0.96×2=1.92decimals:0.0011101011100001010001
0.92×2=1.84decimals:0.00111010111000010100011
0.84×2=1.68decimals:0.001110101110000101000111
0.68×2=1.36decimals:0.0011101011100001010001111
0.36×2=0.72decimals:0.00111010111000010100011110
2. 为了得到有效数字,右移3位,1.11010111000010100011110
有效数字位:11010111000010100011110
3. 第30位是0,指数位填000000112⟶011111002
4. 第31位填0。得到最终的结果即00111110011010111000010100011110
但是这和电脑上的结果最后一位不一样。

00111110011010111000010100011110
(gdb) x/t &value1
0x8049654 <value1>: 00111110011010111000010100011111
(gdb) x/f &value1
0x8049654 <value1>: 0.230000004

当我将其变成双精度存储后,用同样的方法计算,可以发结果是吻合的:

gdb: 0011111111001101011100001010001111010111000010100011110101110001
man: 00111111110011010111000010100011110...


举报

相关推荐

0 条评论