CSAPP-浮点数

本文深入探讨了浮点数在计算机中的表示方法,包括IEEE浮点数的标准形式,以及规格化和非规格化值的概念。通过实例解析了浮点数的二进制转换、阶码和尾数的计算,以及舍入策略。此外,还介绍了浮点数加法的运算步骤,并讨论了C语言中浮点数与整数之间的转换规则。
摘要由CSDN通过智能技术生成

浮点数

二进制小数

限制: 只能精确表示诸如x/2k的数 ,其他的值只能近似表示

IEEE浮点数
数学形式

(-1)s M 2E

符号位s: 确定这个数是负数还是正数

尾数 M:是一个二进制小数,通常规定在范围中[1.0,2.0)

阶码 E: 表示2的幂

浮点数的表示

在这里插入图片描述

浮点数类型

在这里插入图片描述

浮点数表示方法
规格化值

判断条件: exp不(全为0或全1)

阶码字段:阶码字段被解释为以偏置形式表示的有符号整数:E=Exp - Bias

在这里插入图片描述

尾数:M=(1.xxxx…xx)2

规格化实例

值:Float F = 15213.0
那么这样一个数在计算机中如何保存呢?
1 将十进制数写成二进制数
如实例中15213:11101101101101
2 计算M
M为[1,2]的小数,因此为1.1101101101101
3 计算frac
单精度数的frac为23位,因此为11011011011010000000000
4 计算阶码 阶码=尾数小数部分位数+偏置整数(单精度为127 双精度1023)
  单精度阶码为8位 双精度阶码11位
  因此,此例的阶码为127+13=140的二进制表示且位数为8位
  10001100
5 补齐机器码
0 10001100 11011011011010000000000

上机实验

目标:打印出HNU

方法一:使用int型
实验步骤:
int k=0x554e48;
printf("%s\n",&k)
实验结果:打印出HNU

方法二:使用float型
实验步骤:
float f=0x554e48;
printf("%s\n",&f);
实验结果:打印出奇怪的东西
分析:
HNU三个字符对应的ACSII码为	48 4E 55 00
在int中直接保存着48 4E 55 00(图1)
而在float中保存的是90 9c aa 4a(图2)
也就是10010000 10011100 10101010 01001010
可见,对于同一个整数在int和float中的机器码是不同的。
为什么会出现这样的结果呢?
我们按照规格化实例计算一下这个数的由来:
float f=0x554e48;
1 将数写为二进制
0101 0101 0100 1110 0100 1000
2 计算尾数
M=1.01 0101 0100 1110 0100 1000
3 计算frac
frac=0101 0101 0011 1001 0010 000
4 计算阶码 127+22=149 二进制为1001 0101
5 补齐机器码 0100 1010 1010 1010 1001 1100 1001 0000
6 内存查看 1001 0000 1001 1100 1010 1010 0100 1010

在这里插入图片描述

所以,如果想让float和int达到一样的效果,就得做一些变化,使两者的机器码保持一致:
即float表示的机器码为 0000 0000 0101 0101 0100 1110 0100 1000
这个机器码对应的float赋值的数为101 0101 0100 1110 0100 1000*pow(2,-149) 因为这样转换为机器码时才会有上述机器码。
虽然有些复杂,但是在逻辑上还是可以解释通的。这里给一个快速的记法:
给出你想赋值的int,比如说A=0x554e48
要想得到同样的结果,那就 尾数*pow(2,B)
这里的B是(-尾数位数+E(规格化是EXP-BIAS;非规格化是-126))

创造实验

目标:用单精度打印出I miss you
实验步骤:
1 计算字符的ASCII码
十六进制表示:49 20 6d 69 73 73 20 79 6f 75 00 00
2 计算内存存放
内存存放:49 20 6d 69 73 73 20 79 6f 75 00 00
3 内存存放翻译成二进制
  str_0:0100 1001 0010 0000 0110 1101 0110 1001
  str_1:0111 0011 0111 0011 0010 0000 0111 1001
  str_2:0110 1111 0111 0101 0000 0000 0000 0000
4 内存存放翻译成机器码
  str_0:0110 1001 0110 1101 0010 0000 0100 1001
  str_1:0111 1001 0010 0000 0111 0011 0111 0011
  str_2:0000 0000 0000 0000 0111 0101 0110 1111
4 给float赋值
  0xED2049*pow(2,60)
  0xA07373*pow(2,92)
  0x756f*pow(2,-149)
5 结果代码:
#include<stdio.h>
#include<math.h>
int main()
{
float str_0=0xed2049*pow(2,60);
float str_1=0xa07373*pow(2,92);
float str_2=0x756f*pow(2,-149);
printf("%s\n",&str_0);
}
目标:双精度打印GTL
实验步骤:
1 计算字符的ASCII码
  47 54 4C
2 计算内存存放
  0100 0111 0101 0100 0100 1100 0000 0000
  0000 0000 0000 0000 0000 0000 0000 0000
3 计算机器码
  0000 0000 0000 0000 0000 0000 0000 0000
  0000 0000 0100 1100 0101 0100 0100 0111
4 给double赋值
  double name=0x4C5447*pow(2,-1073)
5 结果代码:
#include<stdio.h>
#include<math.h>
int main()
{
double name=0x4c5447*pow(2,-1074);
printf("%s\n",&name);
}
非规格化值

类型一:非规格化值

判断条件:exp = 000…0

阶码值: E = -Bias + 1

尾数 :M = 0,xxx…x2

例子:exp =000…0,frac !=0 是非常接近0.0的数

类型二:特殊值

判断条件:exp全为1

用来表示无法表示的数

一些特殊的值(假设8位浮点数)

格式:s(1位)+exp(4位)+frac(3位)

(以下最值皆为正数)

最小的非规格化数

机器码:0 0000 001 

最大的非规格化数

机器码:0 0000 111

最小的规格化数

机器码:0 0001 000

最大的规格化数

机器码:0 1110 111
舍入与运算

向偶数舍入方法分析

优点:其他的方案都会产生统计偏差

解析:向偶数舍入是针对中间值而言的,对于有偏差的值简单的向靠近的一位舍入或进位即可。

举例:将以下小数舍入到百分位

1> 1.234999 1.23 (后面的数4999小于一半)

2> 1.235001 1.24 (后面的数5001大于一半)

3> 1.235000 1.24 (中间值 向最近的偶数舍入)

4> 1.245000 1.24 (中间值 向最近的偶数舍入)

二进制舍入分析

在这里插入图片描述

无法表示的数

float e=1.1110999f
1 转成二进制
  1.0001110001110001
2 计算M
  M=1.0001110001110001000010110000010100010110110000000011011
3 计算frac
  单精度的frac为23位,因此为00011100011100010000110(向偶数舍入)
4 计算阶码 阶码=尾数小数部分位数+偏置整数(单精度为127 双精度1023)
  单精度阶码为8位 双精度阶码11位
  因此,此例的阶码为0+127=127
  01111111
5 补齐机器码
  00111111 10001110 00111000 10000110
6 查看内存
  内存查看:10000110 00111000 10001110 00111111
7 猜想正确
-------------------下面通过这个机器码倒推二进制数-----------------------
1 机器码转二进制小数
  1.00011100011100010000110
2 二进制小数转十进制小数
  1.11109995841979980469
3 输出未指定时,默认保留6位
  1.111100
浮点数加法

步骤

  1. 对阶,小阶向大阶对齐

将原来阶码小的数的尾数右移|ΔE|位,其阶码值加上|ΔE|,即每右移一次尾数要使阶码加1,则该浮点数的值不变(但精度变差)。

  1. 尾数进行加法运算
  2. 结果规格化并进行舍入处理

如果尾数不是规格化数,则需要进行规格化处理,并进行舍入

  1. 判断溢出

根据阶码来判断是否溢出

C语言中的浮点数

转换

double/float -> int

值向0舍入,对于无法表示或者超出范围的值没有定义

int -> double

能够保留精确值

int ->float

数字不会溢出,但可能被舍入
码小的数的尾数右移|ΔE|位,其阶码值加上|ΔE|,即每右移一次尾数要使阶码加1,则该浮点数的值不变(但精度变差)。

  1. 尾数进行加法运算
  2. 结果规格化并进行舍入处理

如果尾数不是规格化数,则需要进行规格化处理,并进行舍入

  1. 判断溢出

根据阶码来判断是否溢出

C语言中的浮点数

转换

double/float -> int

值向0舍入,对于无法表示或者超出范围的值没有定义

int -> double

能够保留精确值

int ->float

数字不会溢出,但可能被舍入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值