非一般的小数:小数的概念新解、小数分类、浮点数的存储

一、小数的概念

这还用解释吗?小学三年级就开始学,你当我是吃干饭的?
那么,请尝试回答以下几个问题:

  • 所有的分数都是小数吗?
  • 所有的小数都是分数吗?
  • 8.0是不是小数?
  • 8是不是小数?
  • 整数是不是小数?
  • 有理数是不是小数?
  • 无理数是不是小数?
  • 实数是不是小数?

小数可不是看着那么简单,它实在是“非同一般”的数。
先看看小学课本中的定义:
在这里插入图片描述
有点草率哈!也难怪,小学三年级,这样说孩子最容易理解。
再看看百度百科中的定义:

小数,是实数的一种特殊的表现形式。所有分数都可以表示成小数,小数中的圆点叫做小数点,它是一个小数的整数部分和小数部分的分界号。其中整数部分是零的小数叫做纯小数,整数部分不是零的小数叫做带小数。

这里面有两个关键词:实数、表现形式。
“小数,是实数的一种特殊的表现形式”,这句话道出了小数的本质:
(1)小数是数的一种表现形式
什么叫表现形式,就是表现在外面的形式上的东西,它无关本质。好比人的衣服,它并不能真正代表这个人。
好比同样是数字,有罗马数字、阿拉伯数字、中文数字、财务上还有专用的大写数字,它们统统都是数字的表现形式。
因此,说到底,小数只不过是数的一种写法而已。
只要一个数能写成这样的形式:整数部分.小数部分,那么它就是小数。所以说,小数完全是形式化的东西。
(2)只要是实数,都能用小数的形式表现出来
任何实数都能写成小数。
用一张图可以表示出小数与实数范围内其他数的关系:
在这里插入图片描述
比如前面说的整数8,可以写成小数8.0、8.00、8.0000。
这里老金要指出一个关于小数的个人观点,不一定对,仅供参考。
老金认为,在表述小数和实数范围内的数之间的关系时,一定要注意用词。比如我们能说“实数可以写成小数”、“整数可以写成小数”,但不能说“实数是小数”、“整数是小数”。
如果说“实数是小数”、“整数是小数”,那数学就要乱套了。
这就好比我们可以说“数字都可以写成阿拉伯数字”,但不能说“数字是阿拉伯数字”。
因为上图中黑色部分都是根据数本身的不同性质进行分类的,只有小数不同,它只和写法有关,和性质无关。因此,小数和图左边那些数是完全不同的数,不能混为一谈。
所以,前面提的那一大堆问题,只有“8.0是不是小数?”这一条是肯定的。
据此,老金认为,我们小学时都学到的一个说法可能是不严谨的: π \pi π是无限不循环小数。
老金认为:严格来讲,我们可以说“ π \pi π可以写成无限不循环小数”,但不能说“ π \pi π是无限不循环小数”。

二、小数的分类

1.有限小数、无限循环小数、无限不循环小数

按小数点后的数字的性质可分为:
有限小数:小数点后只有有限个数字的小数,如0.5、0.25、1.75等。有限小数都可以表示为两个整数的比(分母不为0)。
无限小数:小数点后有无限多个数字。分为两类:

  • 无限循环小数:小数点后的数字序列中,有一部分数字会无限重复。比如0.333…(或写作0. 3 ‾ \overline{3} 3)、0.142857142857…(或写作0. 142857 ‾ \overline{142857} 142857)。无限循环小数也可以表示为两个整数的比。
  • 无限不循环小数:小数点后的数字序列不会重复,也不会终止。比如π、e(自然对数的底数)、 2 \sqrt{2} 2 等。无限不循环小数不能表示为两个整数的比。

2.纯小数、带小数

按整数部分是否为0可分为:
纯小数:整数部分为0的小数。
带小数:整数部分不为0的小数。

3.定点数、浮点数

按小数在计算机中的表示形式可分为:
定点数:指小数点在数中的位置是固定不变的。因为位置固定,所以实际上不需存储小数点。理论上讲,可以约定把小数点放在任何位置,但通常将其放在最后(纯整数:除符号位,所有数都表示整数)或最前(纯小数:除符号位,所有位都表示纯小数)。
浮点数:小数点的位置是不固定的,这使得浮点数可以表示非常大或非常小的数。
浮点数通常使用科学计数法来表示,即 M × R E M \times R^E M×RE,其中 M 是尾数(或有效数字),E 是指数(或阶码),R是进制。
尾数和阶码均为带符号数。尾数的符号表示数的正负,阶码的符号则表明小数点的实际位置。浮点数的精度由尾数决定,数的表示范围由阶码决定。
如二进制小数10011.101,按浮点数形式可表示为1.0011101×24
其中尾数为+1.0011101,阶码为+4。

三、浮点数的存储

浮点数的存储涉及到补码知识,参见“负数在计算机内的编码”一文。
补码规则如下:

  • 正数补码都是其自身。
  • 负数的补码是对应正数取反,再+1。

此外,还涉及到另一个概念:移码。
移码,就是对补码的符号位取反。假设用4位二进制数表示,-1的补码为1111,移码为0111;1的补码为0001,移码为1001。
0111在十进制下为7,1001在十进制下为9,相当于给原数加上了8(23)。
速算规律:n位二进制数的移码相当于加上2n-1
在C/C++中,存放浮点数的变量类型为float和double,分别占用32位、64位二进制数,其空间的分配规则如下:
在这里插入图片描述
以float类型数据为例,在32位二进制空间中,从左往右:

  • 符号位:最高位为符号位,用来表示正负(0为正数、1为负数)。
  • 指数位:接下来8位为指数位,用来存储浮点数阶码。只是实际存储的数据并非阶码的二进制值,而是将阶码先转为移码,再减1。也就是说,存入的数据为:阶码+27-1=阶码+127)。
  • 尾数位:再之后23位用来表示尾数部分(纯小数)。因为浮点数的尾数都为1.××××,所以可将1和小数点都舍去,只存储小数点后边的部分。

例如,十进制数19.625,若以float类型存储,计算过程如下:
浮点数:19.625转换为二进制数为10011.101,浮点数表示为1.0011101×24
符号位:正数,所以符号位为0。
阶码位:阶码为+4,8位二进制对应存储数据为:阶码+127=4+127=131=10000011B。
尾数位:尾数为1.0011101,舍去前面的1和小数点,只存储后面的0011101。
三个部分合在一起,得到19.625在内存中的存储形式如下:
0 10000011 00111010000000000000000
在这里插入图片描述
若以double类型存储,原理不变,只是指数位变为11位,对应的存储数据为:移码+1023(210-1)。
如十进制数2.5,若以double类型存储,计算过程如下:
浮点数:2.5转换为二进制为10.1,浮点表示为1.01×21
符号位:正数,符号位为0。
阶码位:阶码为+1,11位指数位的移码偏移为1023,1+1023=1024=10000000000B。
尾数位:尾数为1.01,舍去前面的1和小数点,存储为01(之后50个0)。
合在一起,得到2.5在内存中的存储形式如下:
0 10000000000 01(之后50个0)

### 回答1: 在C语言中,我们可以通过使用特定的格式规范符来实现双精度浮点数和十六进制数之间的转换。 如果我们有一个十六进制数,我们可以使用"0x"前缀来表示它是一个十六进制值。例如,0x3F800000代表了双精度浮点数1.0。 如果我们要把一个双精度浮点数转换成十六进制数,我们可以使用"%a"格式规范符。例如: ```c double num = 1.0; char hex[20]; sprintf(hex, "%a", num); printf("十六进制表示:%s\n", hex); ``` 这样我们就可以得到双精度浮点数1.0对应的十六进制数"0x1.0p+0"。 另外,如果我们要把一个十六进制数转换成双精度浮点数,我们可以使用"%lf"格式规范符。例如: ```c char* hex = "0x3F800000"; double num; sscanf(hex, "%lf", &num); printf("双精度浮点数:%lf\n", num); ``` 这样我们就可以得到十六进制数"0x3F800000"对应的双精度浮点数1.0。 需要注意的是,双精度浮点数的精度要比十六进制数高,因此在转换时可能会存在一定的精度损失。同时,在进行转换时,我们也需要确保十六进制字符串的格式是正确的,否则可能会得到错误的结果。 ### 回答2: 双精度浮点数与十六进制在C语言中的相互转换可以通过联合体或指针来实现。 首先,如果需要将双精度浮点数转换为十六进制,可以使用联合体的方法。联合体是一种特殊的数据类型,它的所有成员共享同一块内存空间,可以根据需要以不同的方式解释这块内存。示例如下: ```c #include <stdio.h> union DoubleToHex { double d; unsigned long long hex; }; int main() { double num = 3.14; union DoubleToHex converter; converter.d = num; printf("双精度浮点数 %lf 的十六进制表示为 %llx\n", num, converter.hex); return 0; } ``` 在这个示例中,我们定义了一个联合体`DoubleToHex`,它包含了一个双精度浮点数`d`和一个无符号长整型整数`hex`。首先将要转换的双精度浮点数赋值给`d`,然后通过`hex`成员可以获取该双精度浮点数的十六进制表示。 如果需要将十六进制转换为双精度浮点数,可以使用指针的方法。示例如下: ```c #include <stdio.h> int main() { unsigned long long hex = 0x400921fb54442d18; double *p = (double *)&hex; double num = *p; printf("十六进制 %llx 的双精度浮点数表示为 %lf\n", hex, num); return 0; } ``` 在这个示例中,我们先定义了一个十六进制数`hex`,然后通过将其地址强制转换为`double`类型的指针`p`,再通过解引用`p`获取到双精度浮点数的值。 通过使用联合体或指针,可以实现双精度浮点数与十六进制在C语言中的相互转换。 ### 回答3: 双精度浮点数和十六进制在C语言中可以相互转换。下面是双精度浮点数转换为十六进制的方法: 首先,将双精度浮点数的二进制表示转换为十六进制。双精度浮点数由符号位、指数位和尾数位组成,一般为64位。 1. 获取双精度浮点数的符号位:根据浮点数的最高位判断,0表示正数,1表示负数。 2. 获取指数位并转换为十进制数:指数位一般占用11位,但是由于指数位是以偏移方式存储的,需要减去一个偏移量(1023),得到真正的指数值。 3. 获取尾数位的小数部分,并在开头加上一个隐含的二进制1:尾数位一般占用52位,为了方便计算,需要在尾数位最高位加上一个隐含的1。 4. 将小数部分转换为十六进制:将小数部分乘以16,得到新的小数和整数部分,将整数部分转换为十六进制,将小数部分继续乘以16,得到新的小数和整数部分,依次进行直到小数部分为0或达到所需的精度。 以上就是将双精度浮点数转换为十六进制的过程。 而如果要将十六进制转换为双精度浮点数,则需要反过来进行操作: 1. 将十六进制转换为二进制:将十六进制的每一位转换为对应的4位二进制数。 2. 将二进制位分为符号位、指数位和尾数位:根据浮点数的位数确定每一部分的长度。 3. 将指数位转换为十进制数,并加上一个偏移量:和双精度浮点数转换为十六进制的过程类似,需要还原指数的真正值。 4. 将尾数位转化为小数部分,并去除隐含的1:根据尾数的长度,将二进制数转换为小数,然后去除开头的隐含的1。 5. 根据符号位确定浮点数的正负性。 以上是将十六进制转换为双精度浮点数的过程。 通过以上步骤,我们可以在C语言中实现双精度浮点数和十六进制之间的转换。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

金创想

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值