C语言笔记之数据类型(三)

浮点型数据

一、非整数的表示

除了整数,平时的计算也离不开非整数,即带有小数部分的那些数。在数字系统中,整数和非整数合称为有理数,有理数和无理数合称为实数(好吧,这和本文主旨没关系,但为了显示一下我曾经是数学系的学生。。。)

非整数由一个”.“号来凸显,十进制表示法中,”.“号左边的数字的权为10的正整数幂,幂值按离小数点的距离远近依次为0、1、2...而右边为10的负整数幂,幂值由近及远依次为-1、-2、-3...

例如:12.25 = 1 * 10 ^ 1 + 2 * 10 ^ 0 + 2 * 10 ^ (-1) + 5 * 10 ^ (-2)

二进制表示是类似的,不同的是底数由10变成了2,如:11.01 = 1 * 2 ^ 1 + 1 * 2 ^ 0 + 0 * 2 ^ (-1) + 1 * 2 ^ (-2) =3.25

然而,就像十进制无法精确的表示1/3等分数一样,二进制在表示非整数时,也是近似的。造成这一现象的原因在于,我们书写一个数字时,编码长度总是有限的,比如1/3 = 0.3333333.....我们不可能把3一直写下去;而对于二进制,我们无法用它精确地表示十进制的0.4,我感觉,无论哪种进制,在表示数字时,都是存在缝隙的。

二、非整数的存储

要存储形如11.101二进制的非整数,一个自然的想法就是分为整数部分和小数部分分别存储,这就是所谓的定点表示法——它规定小数点左边几个位用来表示整数;右边几个位表示小数。这种办法很直观,但是效率低下,而且对于超出定点范围的数它只能进行截断,从而无法精确表示。

注意到,一个二进制的非整数,其小数点向左移动一位相当于原数除以2;向右移动一位相当于乘以2。比如:

11.01 × 2 = 110.1;11.01 / 2 = 1.101

再联想到十进制的科学记数法,我们发现二进制也可以表示成x * 2 ^ y这种形式,其中x是一个定点数,它的小数点左边即整数部分只有一位,且只能是1不能是0(因为如果是0,我们总可以将小数点向右移动直到遇见1);右边只能有固定位数的小数部分。y是2的幂,其大小为小数点移动的位数,当然根据向左还是向右,要添加适当的正负号。

这样,每个二进制非整数都要通过移动小数点来变成以上形式(该过程称为规范化),所以这种表示法称为浮点表示法。用这种方法表示的二进制非整数,我们只需要存储三个部分:数的符号,尾数,指数(小数点移动的位数含方向)。

三、单精度和双精度

IEEE定义了几种存储浮点数的标准,常见的两种就是单精度和双精度。单精度格式采用32位即4个字节来存储一个实数(可以包括整数),其中符号位占1位,尾数占23位,指数占8位;双精度格式采用8个字节64位存储一个实数,符号一位,尾数52位,指数11位。

下面谈谈这三部分实际是如何存储的。符号位无需多说,不是0就是1。关键在于指数的存储,它的存储形式决定了尾数的形式。指数存储为二进制时分为三种情况:全为0;全为1;其他。

1、其他情况。。。

和想象中的不同,这种情况下指数并非以二进制补码的形式直接存储的,不过,无论怎样,它必须是一个有符号数,因为显然存在指数为负数的情况。

我们把指数存储的实际位模式当成一个无符号数,其值为e;而指数的实际值为E,那么E = e - Bias,Bias是一个偏移值,大小为2 ^ (k - 1) - 1,k是指数所占的位的长度。计算可知,对于单精度,Bias=127;对于双精度,Bias=1023。

由于此时e不全为0且不全为1,那么E的范围就是:-126~+127(单精度)或-1022~+1023(双精度)。

此时,尾数就只存储小数点右边的小数部分,而左边的部分默认为1。尾数的实际值 M=1+f,其中 f 是将实际存储的位模式转换成小数得到的值,有0<= f < 1 。

2、全为0

这时,E = 1 - Bias = -126 或 -1022;而M = f 。

在上面那种情况下,我们是无法表示0.0的,因为M 总是默认大于等于1。现在,我们只需要将f 的位模式全部置为0,就可以表示0了。然而麻烦的是,还有一个符号位,IEEE规定,+0.0和-0.0并不完全相同。

3、全为1

此时,若尾数的位模式也全为0,则这个浮点数被解读成无穷,根据符号位,还分为正无穷和负无穷;若尾数的位模式不全为0,那么这个浮点数被解读为NaN,即not a number,比如对-1求平方根,就会返回这样的结果。

四、运算与类型转换

1、舍入

由于长度的限制,浮点数只能近似的表示实数。当一个实数超出浮点数所能表示的精度范围时,我们需要一种方式来舍弃一部分数值来得到一个近似值。比如,用2.0来表示1.99999。

IEEE定义了四种舍入方式:向上舍入、向下舍入、向零舍入、向偶舍入。一张图说明(《深入理解计算机系统》):

2、运算

最重要的一点就是:浮点数加法运算不具有结合性!无论加法还是乘法。

比如,假入n是一个非常大的浮点数,那么:3.14 + (n - n) = 3.14, 而3.14 + n - n = 0

其他的就没有什么特殊之处了,只是最后结果可能会被舍入。

3、C语言中的类型转换

C语言提供了两种不同的浮点数据类型:float和double,可惜它们并不总是对应单精度和双精度,因为C语言标准并不要求机器使用IEEE标准。不过大多数情况下,它们是对应的。

下面是类型转换,整形以int为例(以后会补上实验):

int转向float,数字不会溢出,但会舍入;

int、float转向double时,保留精确原值;

浮点型数据转换成int时,值会向零舍入;

double转向float,溢出成为无穷或者由于double精度太高而被舍入。

时间: 2024-08-05 02:20:00

C语言笔记之数据类型(三)的相关文章

Oracle之PL/SQL学习笔记之数据类型(三)

Oracle之PL/SQL学习笔记之数据类型(三) 所有的编程语言中变量是使用最频繁的.PL/SQL作为一个面向过程的数据库编程语言同样少不了变量,利用变量可以把PL/SQL块需要的参数传递进来,做到动态执行程序,同时也可以利用变量在PL/SQL内部进行值得传递,甚至可以把值传递出去,最终返回给用户,由此可见,变量是PL/SQL不可或缺的一部分. 1. Oracle预定义的普通数据类型(常见的数据类型) 类型 子类 说明 Oracle中的范围 char Character,String Rowi

C语言笔记之数据类型(一)

在<计算机眼里的数字>这篇文章中,我曾提到,字节是计算机最小的可寻址的单位,地址对应的是一个个字节,而不是字节的每个位.这样编址的原因很简单--单个的位所能表示的信息量太少了,只有两种状态0和1,只有把足够多的位组合起来才能表示足够丰富的信息.那么为什么一定要是8呢?因为大家都这么做...好吧,肯定一定的历史原因,我就不深究了. 但是,即使是8个位组成的字节,其所能表示的信息量仍然是有限的,因为最多只有256中状态组合,如果用每种状态对应0~255之间的数字的话,那么就无法表示256这个数.这

C语言笔记之数据类型(二)

接下来是不同类型之间的强制转换.当把一个高容量的类型强制转换为低容量的类型时,会发生截断:丢弃二进制的高位,只保留低位(二进制的左边为高位,右边为低位):而把低容量类型强制转换成高容量类型时,会发生扩展:在二进制的高位左边继续填充数字.扩展分为两类:零扩展和符号扩展.下面看代码示例. 1.截断 #include <stdio.h> int main(void) { short a = 257; char b = (char)a; printf("a = %u, b = %d\n&qu

Redis笔记-Hash数据类型(三)

Hash是一个string类型的field和value的映射表. 它的添加.删除操作都是0(1)(平均).hash特别适合用于存储对象. 相较于将对象的每个字段存成单个string类型,将一个对象存储在hash类型中 会占用更少的内存,并且可以更方便的存取整个对象. hset 设置hash field为指定值,如果key不存在,刚先创建 127.0.0.1:6379> hset user:001 name lijie (integer) 1 127.0.0.1:6379> hget user:

C语言笔记1--类型、运算符与表达式

 C语言笔记1--类型.运算符与表达式 总论: 变量和常量是程序处理的两种基本的数据对象.声明语句说明变量的名字和类型,也可以指定变量的初值.运算符指定将要进行的操作.表达式则把变量与常量组合起来生成新的值.对象的类型决定该对象可取值的集合以及可以对该对象执行的操作. 一.变量与常量的名字 1.名字是由字母.下划线和数字组成的序列,其第一个字符必须为字母:下划线"_"被看做是字母,但一般不要以下划线"_"开头. 2.名字区分字母大小写,变量名使用小写字母.符号常量

C++学习笔记之数据类型

一.变量名 几条简单的C++命名规则: 在名称中只能使用字母,数字和下划线 名称的第一个字符不能是数字 区分大小写 不能将C++关键字用作名称 以两个下划线和大写字母打头的名称被保留给实现(编译器及其使用资源)使用.以一个下划线开头的名称被保留给实现,用作全局标识符. C++对名称长度没有限制 二.数据类型 计算机内存的最基本单元是位(bit).字节(byte)通常指的是8位内存单元,可以表示的范围0-255或者-128到127. (1)整型 short至少16位(大多数系统16位,-32768

读书笔记-APUE第三版-(7)进程环境

本章关注单进程运行环境:启动&终止.参数传递和内存布局等. 进程启动终止 如图所示: 启动:内核通过exec函数执行程序,在main函数运行之前,会调用启动例程(start-up routine),取得命令行参数和环境变量.可以把启动例程理解为exit(main(argc,argv)). 终止:五种正常终止方式(从main方法返回/exit/_exit/最后一个线程返回/最后一个线程退出):三种异常终止方式(abort/接收到信号/最后一个线程接收到取消请求). exit与_exit关系:exi

C语言笔记

C语言笔记 基础知识 数据类型 序号 类型与描述 1 基本类型: 它们是算术类型,包括:整数类型.浮点类型 2 枚举类型: 也是算术类型,被用来定义只能使用某些整型值的变量,使用时需要程序员先使用eumn关键字来声明定义 3 Void类型: 用于函数,指明函数的返回值或参数.作用于变量会发生编译错误 4 派生类型: 包括:指针类型.数组类型.结构类型.联合体类型.函数类型 补充:1.函数类型是指函数返回值的类型,数组类型与结构类型统称为聚会类型. 2.除了基本类型,其他的类型都是程序员使用相关关

C++语言笔记系列之十六——赋值兼容规则&amp;多继承的二义性

1.赋值兼容规则 (1)派生类对象可以给基类对象赋值,这种情况下派生类对象将从基类继承的成员的值赋值给一个基类对象:但是不允许将一个基类的对象赋值给一个派生类. (2)可以将派生类对象的地址赋给基类指针,使基类指针指向派生类对象,通过基类指针引用成员时只可以引用派生类从基类继承而来的成员,而不允许引用派生类的新成员. (3)引用与指针相同. 2.多继承 (1)一个派生类从两个以上的基类中去继承. (2)说明: class 派生类名:继承方式 基类1, 继承方式 基类2,...... {派生类成员