粉丝301获赞2521

大家好,今天给大家讲一下浮电素的存储规范 i 一七五四。 好,我们首先来看一下数位的全值,同一个数字放在不同的位置上,代表不同的值,放在个位,他是几个?放在十位呢?他是几十,放在百位,他是几百。 就像我们下面这张表上的这个九放在了十位,它代表的是九十。八放在了个位,它代表的是八。同理,小数点这边也一样, 七放在了小数后面的第一位,它代表零点七六放在了小数点后面的第二位,它代表零点零六。也就说我们一个数字的值可以用一个表达式,就是这个位上的 数值乘以它的全值,然后加上其他个位的数值乘以它的全值,也就是我们这样一个表达式。 我们十进制呢,使用的是逢十进一,所以高位的全值是低位的全值的十倍, 这里是十,这里是一,这里是零点一啊,这里是零点零一,零点一相当于十分之一,零点零一相当于百分之一,那后面呢,就是千分之一啊,以此类推啊。同理,我们的二进制也一样, 因为二进制呢,它是逢二进一,所以高位的全值是低位的两倍。比如我们这里整数部分 就是八四二幺,意味着就说在这个地方有一个一的话,那么就代表实心字的八,所以我们可以看到幺幺零零。其实整数部分呢,它就是代表了一十二。 同理,我们的小数部分呢,既然是高位,是低位的两倍,那么小数部分这里如果有个一,那么就代表零点五,也就是我们的二分之一,那这个是四分之一,八分之一,十六分之一,三十二分之一, 我们可以使用下面的数值,然后乘以这个全值,然后把它加起来,就可以得到这个二进制的值,也就是我们的实进制,通过这个 我们可以很方便的将二进制转发为实进制的值。好,我们在 excel 当中来实际操作一下。好,我们看到下面这个表格啊,在这里我已经把公式写好了, 就说我们来求解这个二进制的值,直接使用他的个位上的数值乘以他的全值,然后把这些加起来啊,公式在这里 啊,比如我们现在要求幺零幺点幺幺,那么我们直接写上,把这里改成一,这里改成一,这就是幺零幺 啊,那么这个写上就等于五,然后啊幺幺,那么我们在这里写上就好了,一一啊,就是等于五点七五。 好,那么我们再来求一下,零点幺零幺 啊,等于零点六二五, 总的来说就是我们二进制转换为十进制是非常的简单啊,那么难一点呢,就是我们的十进制如何转换成二进制?我给大家举个例子,比如我们这里来计算五点六二五的二进制 啊,第一步呢,我们将这个数分成整数五,还有小数部分,零点六二五,先来处理这个整数部分,整数部分呢,我们使用的方法 叫除二取余法啊,我这里呢其实可以直接将这个五写到这里,就是幺零幺啊,这个因为我们前面计算过,这个五得二进资就是幺零幺 啊。当然呢,我们现在计算之前,我是不知道每一位是多少的,我只是先把它放到内存当中,方便我们观察。 好,我现在来除以二,除除以二的话,它其实应该等于一个二, 然后余余上一个一。好,我们取的就是这个余数为一,就得到一个结果了,这事实上就是他的最低位。为什么会这样子呢?我们想象一下啊,二进制,他是一个逢二进一 一的这样一个过程,也说这是最低位,那么上面的都是二的倍数,只有这一个位没有满足,就是进一的那个条件,所以这就是余数上面都是二的倍数,所以除以二以后,取这个余数就可以取到他, 这样就非常简单。那他的三是多少呢?等于二,其实就是这个一零,当他现在不是一零,他现在是四。但我们想象啊,除以二,是不是这个这一位要移到这里来,二就变一了,四就变成二了,所以呢,这个一零就移到这里来了, 这里变成零,这里变成一,这里就变成零。哎,这就是除以二以后的结果。同样的,我们再次除以二,就是这个二除, 除以二呢,它肯定是等于一个一的,然后它的余数为零,也就是把这一位求出来了。其实除以二呢,它就是一个向右移动移位这样一个过程, 然后这一位原本的这一位呢,就是他的余数,也就是我们要得到的结果。好,现在再移一次,然后他变成零,他变成一, 那么一再除以二呢?显然它的这个三呢,等于零,余数等于一,这样子我们就求出来了,等于一零一 啊,这个我写一下,就是说整数部分使用的是除二取, 那么我们的小数部分呢?零点六二五,怎么样把它变成一个二进制呢?我们来操作一下, 我们先把整数部分拿掉,这边呢,其实就是零点六二五啊,我们也是不知道的哈,我们不知道的,我们这里只是为了方便看到他。 嗯,我们这边呢小数部分使用的方法叫乘二取整。 我们想象一下,这边有个数据,我要乘以二,那是不是每一位都要乘以个二? 比如我们这个零点一二五乘以二就等于零点二五,零点二五乘以二,他就等于零点五。 这个零点五乘以二呢,它就等于一,这个是实际上就是二分之一。我乘以一次二的时候,如果说它等于一的话,那么原先这里肯定是为一的,因为你有个零点五乘以二才可能变成一,对吧? 也就是我们的这个乘二的过程,其实上是一个左移的一个过程,乘以二就左移一次,哎,就把它这里变成了一,这里变成了零。 零点一二五乘以二等于零点二五,就变成这个样子,那他的整数是不是就是我们的原先的第一位?哎,这个时候我们就可以得到一,当然呢,我不知道这个数字哈,我就直接用这个零点六二五来乘,那乘以后的结果其实就是这个, 这是一点二五,一点二五,好,我们这里乘以二等于一点二五, 然后有个一,我们是不是就可以取出来了?也就是这个一,好,我们把它变成零。 剩下的小数啊,就是零点零一,零点零一,我再次乘以二呢,那这个零就到这里来了,这个零点二五啊,就变成了零点五,好,我们就变成一,这个也变成零了, 也就是我们这里剩下的一部分,要把这个一减掉,零点二五,再乘以二,等于零点五, 显然这个地方没有等于一,那么它应该等于零,我们的第二 位,也就说最开始的时候,这个地方是四分之一位的时候,我乘了两个二吗?四分之一位,如果说他是一的话,四分之一乘以四就应该等于一,他现在没有等于证明当时他这个地方是零。 ok, 我们就得到这一位, 那么下一位呢?我们再次乘以二,就把这个一乘到这里,放到这里来了。好,这个时候变成零,好,我们这里就变成了一点零, 最终的一就取到了,那这个是最高位,然后第二位,第三位 也说不断的乘二,就是不断的向左移动,然后在这个地方取出这个整数, 所以叫乘二取整,那么最终的结果就是一零一点,一零一,这个顺序是相反的啊,就是我们整数的部分呢,它是 先求出来的是低位,然后求出来的慢慢的高。而我们的小数部分呢,他是先求出来的是高位,然后再慢慢乘起来,最后乘到最后才是我们的低位 啊,当我们的小数部分啊,没有的时候,那么我们的乘法就结束了,因为后面没有小数了吗?所以就是这样的。 当然呢,我们不需要每次来进行这个计算,我写了一段程序,它可以自动的帮我们整数部分也好啊,小数部分也好,自自动的求出来,然后自动的合成一个二进制。 好,我们来看一下。好,我们现在看到的就是十进制辅点数转二进制的代码啊,因为比较简单,刚刚也是做了原理,所以我们这里就快速过一下。首先呢,我们将这个数拆分成两部分,一部分呢是整数部分,一部分是小数部分, 然后我们准备了一个支付串来接收。最核心的呢,就是在这个地方,我们进行了一个除二,然后取他的余数的一个过程,这是余数,这是除二,然后反复的除, 直到它等于零,那这个就整整数部分的代码就这一块, 那下方这个呢就是乘二 二取整法来解决小数部分啊,方法就是乘二,在这里不断的乘二循环嘛。然后呢,我们可以取他的整数部分,那这就是取整数部分啊,代码非常简单。好,我们来预醒一下, 我们依依然以刚刚的例子啊,五点六二五,我们显示编码还可以看到等于幺零幺点幺零幺,那这个就是二进制的结果 啊,但是这个只是数学意义上的二斤制,并不是存储到计算机里面的二斤制,所以这个时候就涉及到我们今天要讲的主题, i e e e 七五四这个标准。 好,我们来看一下 ppt, 七五四的编码呢,分为三部分,第一部分是符号位,第二部分是接码,第三部分是尾数。 符号位用来存储这个数据的,正负零代表正数,一代表负数。 尾数部分用来存储数据的有效数字位,也就是精度位数。 接码用来存除有效数字乘以二的次数,也就是决定小数点 点的位置。默认情况下,小数点的位置实际上是在尾数的这个地方,也就是说这里隐藏了一个一点,然后和我们的尾数哎,合成一个有效数字。 好,然后我们再来看一个公式,这个公式呢,是用来计算符点数的值的啊,当然我们这里是以 a float 类型为例,使用我的这个工具来对照着来学习。 我们这里以十二点二五为例,我们显示他的编码,哎,这个在前面已经说过了,就是我们计算出来的,那下方这个就是七五四的编码,这个编码 是我们计算出来的,而下方的这个编码呢,是我们在内存当中读取过来的,也就是说实际的存储就是这样子,那么我们要研究的就是如何从一个数学的二进制转换成一个存储上的一个物理上的一个编码标准, 他们之间有什么样的映射关系啊?第一位是符号位,很好理解 啊,当我们为零的时候,我们这个公式里面任何数的零次方都等于一,那么代表正数,如果是一的话,那么他就是负一,那么肯定这个数据就变成了负数 啊。第二个呢是指数位,嗯,这个地方呢,我们先看一下上面这个编码,他现在这个编 码呢是不规范的,我们要转换成七五四编码的时候,先要将小数点移到这个位置,就是变成一点几几几,那么我们这里要移动三位, 我们向左移动三位一二三,那这样子就变成一点一零零零一 啊,这事实上他是缩小了,变成原先的八分之一,也就是说啊,他除了三次二,那么我们实际获取这个数值的时候,显然要乘以八乘三次二, 那这个次数,哎,我们就是存在这个指数位里面,也就是我们的接码里面,这里要存储一个三,但显然我们这里这个数字存 组的不是三啊,这个也就是我们 i 一七五四的一个编码规则,我们这个接码要加上一个一百二十七,当然还是以 float 为例啊,如果是 double 的话,要加上一个一零二三,就是一千零二十三 啊,他为什么要加一个一百二十七来存储呢啊?这是为了将负数方便的存储,我们可以观察一下这个指数位,指数位一共有八位啊,他的范围应该是零到二五五 啊,但是零还有二百五十五呢,有其他的作用。我们真正存储指数的时候,使用的是一到二百五十四, 一代表的是负的一百二十六,因为负的一百二十六加上一百二十七就等于一,那么最大的是多少呢?最大的就是一百二十七,也说我们的指数范围是负的一百二十六到正的一百二十七。 我们的指数位其其实上就决定了这个小数点的位置,如果它是正数,代表着我们小数点要向右移动,比如这里是三,那么我们 这里代表的数值是多少呢?是一点一零零一,然后他小数点向右移动三位就还原成我们之前的那个二斤之数值了,比如说这边缩小了,那我这里也要向右移动三位, 把它变大。那么我们再来看一下这个尾数,这边尾数这边最大的一个特点呢,它就是少了一位,它存储的其实是小数点后面的这个数字,为什么前面这个一点不存储呢? 因为我们几乎所有的这个小数我们都可以变成一点叉叉叉,那么我们前面这个一点如果要存储的话,那就浪费了一位,所以这个一都是相同的,肯定就不用存储了。 我们使用的时候直接在这里想象一下,这里有一个,把它加起来合成一个真正的有效位数就 ok 了 啊,那这个呢就是我们七五四的编码规则,那么我们随便再试一下,重新 新用一个,比如五点六二五,好,我们显示编码他是一零一点一零一,那么我们首先要将小数点向左移动两位一二就变成了规格化的小数了, 那么这个地方我们存储的时候就要有一个二要向右移动两位二加上一百二十七,等于一百二十九,所以这里面存储的数字是一百二十九,实际使用的时候它会自动的减去一百二十七。符号位是零 啊,我们可以把它改成一试验一下,然后我们生成复点数,这就是负的五点六二五,所以这就是符号位的作用。这边呢是 剩下的就是零幺幺零幺,它存储到这里的就是零幺幺零幺,那后面还有多少零呢?这个根据我们的数据空间,比如 float, 它一共是二十三位, 而我们的 double 呢,它这边呢应该有五十二位啊,实际的有效数我们这里是二十四位,要加一位,因为前面这里有个一嘛小数点的位置在这里,但是我们要移动两位, 它会变到这里,移动两位以后就是幺零幺点,幺零幺就是我们之前那个二斤支数 啊。现在我来给大家解释一下,就是我上一个视频当中说的就是,嗯,为什么浮点数不能够精确的存储?我们可以试验一下零点一 啊,显示编码,其实这个编码我们这里是控制了它的位数的,否则的话这里是显示不完的。我们观察一下这个, 我们使用乘二取整法,算下来以后,他会不断的循环,不断的循环,就说零零幺幺,零零幺幺零零幺幺,这样子不断的循环下来, 也就是说我们用二进制式没办法准确的表示零点一的, 因为我们内存空间是有限的,但是他这个小数,这样子,我们不断的乘二取整,不断的乘二取整,那么他是永远不会结束的,就是算不断的,那么这个情况下我们只取了其中的一部分, 那比如像我们这里小数点向右移动,一位、两位、三位、四位,那么变成一点一,零零一一等等等等,然后取了二十三位就取到这里,那这个就是我们这里。但是有一点要注意一下,发现我们最后一点有一点不一样,一个是零,一个是一, 那这个是什么原因呢?这个就是一个进位的一个啊,我们可以想象一下,在他的这一位的后面,这里有个一,就代表有上一位的二分之一。 我们不是说啊,四舍五入吗?那他现在已经有个五了,相当于有了一半的值,然后后面还有一些一,那么他是超过五的,所以我们通常情况下这里应该要加一啊,这样子 这个数值更接近这个零点一,这里有一个进位,有一个进位,同样的零点二,他也是不能够 啊精确的表达,这里面呢也是使用了一个近视的截取啊,这个地方呢也是进了一个一啊,其他的位都是一模一样的。 好,我们这个 i e 七五四的基本的这个编码规则就看到这里。最后呢我们来看一张表, 通过这张表我们可以更加清楚七五四的标准啊,当然我们还是以 float 为例,嗯,我们前面看过的这些二金字形式呢,其实是叫规 约形式,也就是说比较规范的,最大的特点呢就是他的小数位,就是这边这个小数位,他是在一到二之间的,比如说一点几几几的这种的小数,这就属于规约的。那其实还有一类就是非规约的, 非规约的它这个要求指数这里全零,我们将指数改为全零 啊,八个零,那这个时候生成的数,这个就属于非规约的, 我们可以看到指数比三十八要大,因为这里是零点几了, 有效数字它是在零到一之间,而不是一到二之间 啊,这是一个特殊情况,我们刚才说了指数啊,零是一个特殊的用处,他就是用来显示非常非常小的一些数字,那么最小的是哪个数字?我们把这个变成零, 这个变成零啊,当我说的是不是零的那个最小数字哈,就是这里唯一的时候,这就是最小的,我们可以生成一下啊,四十五次方了啊,非常非常的小。 另外呢就是说我们如果说全部都是零的话,那这个时候,哎,其实就是我们的零零的表示方式,我们可以生成一下,它就是等于零 啊。当然呢我们这个非归约的这里面呢是有很多很多的数字,这个地方啊,他显示的是零,但是实际处理 的时候,他相当于是负的幺二六,就说小数点要向左移动一百二十六次,他本来在这里吗?要往这个方向移动一百二十六次,可以想象前面有一百二十六个零, 然后加上这里还有二十几个零,这个数有多小?所以我们这里一个一,当时是非常非常小的啊,当然他都是非常靠近零的,用这个非规约的形式来表达非常非常小的数字,是非常方便的。 嗯,除了全零以外,我们还有一种情况,就是全一,如果我的指数位这里全一,我们可以输入一下一二三四五六七八啊,就是八个一,那这个时候我们 生成浮点数啊,他是正无穷啊,注意一下,必须要求小数也是我们的有效数字,这里全零,那他就是代表无穷啊,如果符号位变成一了,那就是负无穷 啊,这就是无穷的啊,在我们计算机里面的表示方式啊。那如果说这个时候我们指数全一,这个时候任意一个我们有效数字变成一的话,那么这边呢,就会变成 n a, n 就是 nato number, 也就是一个无效的数字,任意一个啊,不止这里,你把这边改一个一也是一样的,它都是啊,无效的数字 nato number。 嗯,好,我们这里介绍了一些特 数的情况,就说无穷大,无穷小零,还有无效的数值,还有非归约的这种形式,就是零点几几几,然后乘以二的负一百二十六次方,非常非常小的一些小数,他的表示方式 啊,那么我们这个介绍七五四的标准就讲到这里啊,希望对大家啊有所帮助。

无论是单金度福点、 float 还是双点 double, 他们在二建制存储的时候都是由三个部分组成的符号,接和尾数,那我们来看看他们是怎么做的。那么我们的 float 是三十二位单金度福点, double 是六十四位双金度福点, 那么同样符号通常是一位。而在四字节的 float 和八字节的 double 当中,接的位数分别是八位和十一位,而尾数 float 二十三位, double 高达五十二位。 接下来我们来看一看如果遇到一个具体的数字 flowtop double 是如何来处理的。那么刚才我们既然设计到了三点一四派,那么就来看看三点一四是如何被放到这个二进制数据当中去的。其实这个过程并不复杂。好, 三点一四,三点一四,整数部分三,小数部分零点一四,三点二,建制码是零零幺幺,没有问题。幺四呢啊,如果你把它看作十进制数据十四的话,他的代码是幺幺幺零。难道三点一四就是把这两个二进制接拼起来就是三点一四二建制吗? 显然这是不可以的。怎么办?整数部分零零幺幺没有问题,关键是少数部分。 那么小数部分该怎么做呢?让我们知道小数部分采用连续的短乘法来完成十进制到二进制的转换。 首先将零点一四乘以二,注意只乘小数部分。当然了,零点一四也没有整数部分,乘以二的结果是零点二八。接下来将零点二八的小数部分再乘以二,注意只乘小数部分。零点二八的小数部 乘以二,得到零点五六。零点五六的少数部分再乘以二,得到一点一二,一点一二再乘以二,注意不要考虑整数部分一,而是把右边的零点一二乘以二,一直这样做下去, 一直这样做下去。好,我们会看到这个过程无穷无尽,无穷无尽。那么大家注意,基本上一个位数有限的十定制数字,除非非常巧合的情况下面他要变成二进制数字,通常都会变成一个无限的循环小数。 比如说这个数据现在在某一段上面就是循环的,在哪里循环呢?咱们仔细找找看。零点五六,注意第一个数字零点二八,第二个数字零点五六,找哪里又出现零点五六了?对,二十二四十二六十二都出现了零点五六, 所以这个数据的循环体长达二十位。当然不管这个数字喜不喜欢,我们只提取现在这个连续惩罚所得的整数图和 把整数部分提取出来,就是零点一四转成二,进至小数的结果。就我们来看,零点一四转成二,进至小数是零点多少呢?首先把第一个数据零点二八的零提出来, 然后再把零点五六的零提出来,接着把一点一二的一提出来,依次一个一个的放到我们的数据当中。 好,现在我们的小数部分已经转化完了,很显然这是一个无穷无尽的数据,然后再加上整数部分,整数部分三,也就是幺幺,把整数合进我们的小数部分,就像这样 好,于是这个数字三点一四转化成二级级小数,就是幺幺点零零幺零零零零幺幺幺幺零幺等等等等等等等等。 接下来要将这个数字写成二进制的科学技术法,二进制的科学就用法和十进制的科学技术法一样,移动一下少数点,然后往左移的话,每一位就乘以二的正的多少次方,那么往右移位,那么就会变成二 二的负的多少次方。原则是只要这个数字不是零,那么就在小数点的左边只保留一个一,也必须有一个一好的,那么这个数字很简单,小数点向左移一位,那么变成乘以二的一次方。 好,现在我们这个数据就可以放到四字节的 float 的数据当中去了。怎么放呢?我们来看看单精度福点四字节 float, 他是由一位的符号八位阶和二十三位尾数来组成的。首先看符号为 s, 符号为 s, 零表示正,一表示负和。想要这个数字是正的,所以 s 直接填上 零表示正数。接下来接的八位也就是这里的一。那么注意了,这里面存储接一,也就要存储二的一 次方的那个蓝色的依次方。那么依次方是直接放进一里面吗?不是的,按照规定,一必须加上了一百二十七,然后再存进这八位的一里面。至于有同学问,为什么是要加一百二十七呢? 嗯,这是一个固定的数字一百二十七,也就是这是单金度福田 float 的格式的要求,一定要加一百二十七,一百二十七就固定数字,如果是 double, 那就是加一零二三。好,一加上一百二十七等于一百二十八,一百二十八的二进制就是一 七个零,然后将这一七个零刚好八位放进我们的一当中去。由于几乎所有的二进制小数只要不是零,他的二进制科学技术法一定能写成一点多少多少乘以二的多少次方。对啊, 二进之数字不是零就是一。所以啊,在 iq 的标准辅点格式当中,小数点前面那个一根本就不存储,所以在这里我们不会将一点一零零一全部放入尾数 f, 而是将一点去掉,将点号后面的幺零零幺零零零幺幺幺放入。哎,好,当然在这里边我们先截断一下,先截出二十三位来,然后逐渐的将这二十三位放入我们的尾数 f, 分三组逐一放入。好,大家注意前面的一点多少是不放进去。好了,这就是我们三点一四用三十二位四字节谱点,单精度谱点来表达的结果。所以 如果在 c 加加当中写下代码 flowto x e 等于三点 s f 的时候,那么 x e 章中所纯属的数据就是这样子的。 那么注意,这 f 表示这是一个福点,单金度福点的字面常亮,而不是 double, 那么当然三点一四如果复制给 double, 那么在内存里边它是这个样子的。注意,它的 d 变成了十一位, f 变成了五十二位。

这个视频将会分析 c 元浮点数类型的底层原理。首先我们看三个问题, float f 一等于二点二,判断 f 一是否等于长数二点二。 float f 二等于二点二,判断 f 二是否等于长数二点二 f, float f 三等于二点二五,判断 f 三是否等于长数二点二五。 正确答案是, f e 不等于二点二, f 二等于二点二, f f 三等于二点二五。视频后面内容会分析具体原因。 c 元辅点数 包括四字结的佛奥特类型和八字结的 double 类型。对于浮点数,我们必须有一个重要的概念,就是绝大多数的浮点数是不能精确表示的,下面看一下具体原因。 一般 int 类型和 float 类型都是三十二位,计算机中三十二位最多,表示二的三十二次方个数。 int 类型的取值范围是负的二的三十一次方到二的三十一次方减一。 inter 类型取值范围中的每一个数和这二的三十二次方格数是一一对应的,所以 inter 类型是可以精确表示的。 flot 类型的取值范围远大于 inter 类型,而且 float 类型的数有无限多个,所以 float 类型中的数无法与这二的三十二次方格数一一对应,只能是多对一。 因此大多数弗洛特类型的数不能精确表示,只能近似表示。计算机采用的是 i e e e 七五四浮点数格式标准, f 等于负一的 s 次方乘以 m, 再乘以二的 e 次方。 s 是符号, e 是指数, m 是尾数。 flot 类型使用一位符号八位指数,二十三位尾数。 w 型使用一位符号,十一位指数,五十二位尾数。现在我们看一个例子,我们看 类型的数,九点六二五在内存中是如何存储的。第一步,将十进制转化为二进制,整数部分九就等于幺零零幺,比小数部分采用乘二取真法, 零点六二五等于零点一零一 b, 所以九点六二五就等于幺零零幺点幺零幺 b。 第二步,确定符号指数和尾数幺零零幺点幺零幺等于负一的零次方,乘以一点零零幺幺零幺乘以二的三次方, 所以 s 就是零 m 等于一点零零幺幺零幺亿 等于三,实际中存储的是 s, 依然是零 m, 需要去掉整数,尾只存储零零幺幺零幺 e, 要加上偏移幺二七,实际存储的是幺三零, 最终存储在计算机中的数用十六进值表示,就是零 x 四幺幺 a 零零零零。我们可以用网络上的一些工具来查看浮点数的实际存储值。 r e e 七五四 converter 可以方便的查看指数和尾数的每一个位。 base converter 可以对比 float 类型和 double 类型实际存储的值。我们来看浮点数二点二,二 点二是不能精确存储的, flow 类型和 double 类型实际存储的值是不一样的,我们可以用 c 元代码查看复点数真正的存储内容,我们可以用 pretf 百分号点三零 f 来对比二点二 f 和二点二的内容, 结果和网上的工具是一样的。我们也可以查看浮点数在内存中的实际的二定制内容。首先取浮点数变量的地址, 再强制转化为 inter 指针,再用星号取得整数的值,这样我们就可以得到浮点数在内层中真正的存储的二定制内容,感兴趣的可以自己试一下,并和 网络上的工具的结果做对比。我们再来看视频开始的三个问题,思域元的浮点数常数默认是 w 类型, 二点二是不能精确表示的, float 类型的二点二和 double 类型的二点二是不相等的,所以 f 一不等于二点二, f 二等于二点二, f 二点二五是可以精确表示的, float 类型的二点二五和 double 类型的二点二五值是一样的,所以 f 三等于二点二五。 我们在使用辅点数时,要考虑辅点数的精度问题是否对我们的程序结果产生影响,理解其原理,灵活。使用好了这个视频就到这里,欢迎关注、点赞、转发,我们下个视频再见!



今天呢,我们来看一下 c 语言中辅点数精度的问题。首先呢,我们来看一个例子,在这个例子里面呢,我们定一个变量, a 为 flow 的类型,并且呢给他复制为二十五点五八五,然后呢输出 a 的值, 在屏幕上呢,我们会输出这样的一个结果,二十五点五八四九九九,这是为什么呢? 他的原因呢,是由于辅点数类型在内存当中存储会导致数据精度的丢失而造成的。 下面呢,我们来看一下刚才我们在程序当中的那个数是如何来存储到内存当中的。首先呢,我们知道 flow 的类型在内存里面呢 占四个字节,也就是三十二位,根据 i 一一七五四的标准呢,我们将三十二位呢划分为三个部分,分别呢一个二禁止位表示符号位,八个二禁止位表示指数位。 二十三个二径之位呢表示尾数位。然后呢,我们将二十五点五八五转换为二径之数。首先呢,我们先转换整数部分, 二十五,根据二进制转换的原则,转换的二进制为幺幺零零幺。然后呢,我们来将 小数部分零点五八五转换二斤之数。首先呢,我们将零点五八五乘以二,得到一 点一零七,然后呢取整数部分的一,我们写到这里来,接下来呢,我们再取一点一零七的小数部分,再乘以二, 得到零点三四零,取整数部分零。然后呢,我们再将零点三四零的小数部分乘以二,得到零点六八零, 然后呢取整数部分的零,以此类推呢,我们就可以得到零点五八五的二净之数, 从上到下依次就是零点一零零幺零幺零, 很显然这个数呢是一个无限的数, 所以说呢,我们将二十五点五 五八五转换为二,近日数呢就是幺幺零零幺点幺零零幺零幺零。由于 i e e 七五四,他的尾数部分, 我们要放二十三个二进制数,所以说呢,我们将我们刚才球的这个小数部分呢,我们再进行扩展。 然后呢,根据 i 一七五四小数部分存储的原则,我们要将刚才转化的这个二定制数转变成一点叉叉叉乘以二的 n 次方这样的一个结构,也就是呢,我们要将这个数的小数点 向前的移动四位变成一点一零零幺乘以二的四次方这样一种形式。 然后呢我们再把它呢放到 i 一七五四对应的每一个部分去。首先这个数呢,它是一个正数,我们根据零为正数,一为负数的原则,所以说符号为我们填零。 然后呢我们再看这个数的指数部分,他是二的四次方,所以说呢,我们将四转换为二进制数是幺零零,根据我们指数位一共有八位,所以说前面再补五个零,我们就可以得到八位指数位。 最后呢,我们将二十三位二进制数填到尾数位里面去,大家可以看一下,在我们这个里面我画白框的呢,就是正好是二 二十三位二斤之位,我们填到我们这个尾数位后面,在很显然在后面呢还有一些尾数,我们并没有填到尾数位里面,这个时候呢就发生了数据截断的现象,所以说呢,会导致我们这个数精度的一个丢失。 最后呢我们根据二十五点五八五转换到内存当中的表示,是这样的一种形式, 然后呢我们将这个数转换为实净质数,最后就得到二十五点五八四九九九,并不是我们之前的二十五点五八五, 这就是由于我们这个数呢在存储的时候呢发生了截断,所以说呢会导致 精度的一个丢失,这就是为什么二十五点五八五在内存里面存储的时候和输出的表示形式不一样的一个原因。


同学们大家好,那么在上一节课呢,我们就学习了定点数的加加法运算,那么在这节课呢,我们再来学习辅点数的加加法运算。那么在前面我也介绍过了,任意一个辅点数呢,都可以表示成这个 s 乘以 l 的这次方这样子的一个表示格式,那么在这里面 s 呢是尾数, l 呢是这个基数,而这个 j 呢是这个接码。 那么接下来我们就来学习辅点数的加减法,在这里面我们使用 s 和 y 这两个数来表示辅点数一以及辅点数二。那么对于辅点数的加减法运算,主要有几个步骤,首先呢,第一个步骤是需要进行对接的操作, 接着是第二个步骤,进行尾数求和的这个操作,接着第三步需要进行这个尾数规格化的操作,接着第四步呢,需要进行手游的操作。最后第五步需要进行 出判断的操作。那么在这里面各个操作是什么意思?具体的流程是什么样子的?我们接下来再通过 x 和 y 这两个例子来进行相关的学习。 在下面的这个例子里面, x 等于零点一一零一乘以二的零一次方, y 呢等于负的零点一零一零乘以二的一一次方。首先我们来看加减法的第一个步骤,对接, 那么为什么要对接呢?其实对接的目的就是使得两个符点数的接码一致,使得尾数可以进行计算, 因为伏点数的尾数运算其实是简单的,这个尾数的伏点运算就是我们前面所学习的定点数的运算,但是呢,伏点数的位数实际的少数位与这个接码有关,如果这个接码不整齐的话,那么伏点数的尾数是无法进行直接的运算的,所以我们在进行辅点数 运算的时候,首先要进行对接的操作,而对接的操作呢,接码按照这个小接看齐大接的这个原则。什么是小接看齐大接?我们接下来就通过个例子来理解这里面对接相关的内容。 对于刚才的 x 和 y, 那么在计算机里面可能是这样子存储的,分为四个部分,接码的符号位,接码的数值位,尾数的符号位以及尾数的数值位。 在这里符号位呢使用的是双符号位,因为我们要进行溢出的判断,而这个数字位我们使用的是四位的存储,包括这个接码的数字位以及尾数的数字位。那么对于什么是小接看起大接,我们来看 x 的接码以及 y 的接码,那 x 肩膀呢等于零一,而 y 的肩膀等于一一,那么要按照小肩看起大腿的操作呢, x 的肩膀就要变得和 y 的肩 码一样,所以这个时候我们就需要进行 x 转换操作, x 结码变成一一,这个时候尾数要右移两位,而 y 呢则不用变,进行 x 的右移之后, 我们就把这个接码给对齐了, x 接码等于零零一一,这个 y 的接码呢也是零零一一,并且在这里面因为这个尾数又移了两位,并且由于这个尾数的存储是四位的存储, 对于这个 x 尾数的优移,使得 x 的尾数由四位变成了六位,这个时候我们需要进行舍去的操作,舍去最后两位零一,所以这个时候 x 的尾数呢就变成了零零一一,那么这样子呢,我们就完成了对接的操作, 接着就进行第二个步骤,尾数的求和。那么尾数的求和和上一节课的定点数的加减法运算是一样的,使用补码来进行运算, 对于减法运算呢,将会转换成加法来运算。同样的我们来看刚才的例子,那么对于刚才对接的过程,对接完成之后呢,我们就得到了 x 和 y 这样子的值, x 呢等于零点零零一一乘以二的一次方, y 呢等于负的零点一零一零乘以二的一一次方。 接着我们就可以进行尾数求和的操作了,而这个尾数求和呢,就和上一节课的定点数的加加法运算是一样的,我们首先要求出 x 和 y 的补码, 接着我们连同符号位和数字位一起进行运算,就得到 x 加 y 的补码等于一一点一零零一,所以我们就得到了 x 加 y 的补码的结果。 接码的符号位是零零,尾数的符号位是一一,而这个接码的数值位呢是零零一一,尾数的数值位是一零零一。在这里求得了 x 加 y 的和的补码 s 之后呢,我们就完成了尾数求的操作,那么尾数求和之后呢,就可以进行第三个步骤操作了。第三个步骤,尾数的规格化。 那么对于尾数的规格化呢,需要判断两种情况, s 大于零的情况以及 x 小于零的情况。那么在这里面列举了 s 大于零的情况以及 x 小于零的情况, s 大于零,如果尾数不满足零零点一这样子的格式的话,那么就需要进行尾数的规格化,同样的,对于 s 小于零的情况,如果 s 不满足一一点零这样子的格式的话,那么就需要进行规格化的操作,我们也可以这样子简单的记住这种格式,那就是 符号位与最高位不一致,如果尾数不满足这个符号位与最高位不一致的情况,我们就需要进行尾数的规格化。接下来我们再来进 继续刚才的这个例子来学习这里面的尾数规格化。那么在刚才呢,我们已经求得了 x 加 y 的补码的和等于这个一一点一零零一,我们把这个 s 与刚才的这个约束进行一个对比,发现并不满足这里面的任意一种格式,所以呢,这一个尾数是需要进行规格化的, 我们把它规格化进行左移移位,那么左移之后呢,我们就需要把这里面刚才的最高位的一给舍去掉,并且在最后加一个零,那么这样子呢,就完成了这个尾数的规格化的操作。同样的做了这个左移的操作之后呢,接码也需要相应的减一, 那么这个时候就得到了接码的数值位变成了零零一零,而这个尾数的数值位呢就变成了零零一零,可以看到尾数左移之后呢,接码呢也要减一好的,那么这个 时候规格化之后,我们就得到了 s 等于一一点零零一零,现在我们可以求出他的原码是负的零点一一一零,所以刚才的 x 加 y 呢,就等于这个负的零点一一一零。 别忘了我们还有一个接码的操作哦,那么这整一个呢,就是刚才 x 加 y 的结果,对于尾数的规格化,一般情况下呢,都是通过左移来完成这个尾数的规格化的,当尾数不满足这样子的格数的时候呢,就需要进行左移来完成规格化的操作。 但是有一些情况呢,是需要进行右移来完成尾数的规格化的,当双符号位不一致的时候,就需要进行右移来完成尾数的规划操作。这个双符号位不一致,在定点预算的时候呢,就属于溢出的情况,但是这样子的情况呢,在伏点运算不算这个溢出的情况,至于为什么不算呢? 后面会介绍。而进行右移来完成这个尾数规格化的时候,就需要进行左右的操作,那么什么是这个左右的操作呢?我们接下来先来了解什么是左右的操作,再来了解这个右移规格化的过程。对于这个手右操作,在使劲制的时候呢,手右操作有一个叫 四舍五入的方法,那么在二进制里面也有一个类似的方法,叫做零舍一入法,零舍一入法就是相当于二进制的四舍五入法。那么零舍一入法怎么操作呢?我们先来看一个例子,假设这里面 s 等于一零点,一零一,一零一一一,那么这个时候符号位为一零一 一零呢,双符号位不一致,所以我们需要进行右移的操作,而右移之后呢,我们就得到了这个一一点零一零一一零一一,因为我们进行了右移的操作,而最末尾的一呢,我们是没办法存储的,所以 所以我们就需要进行所有的操作,因为舍弃的这个是一,所以呢我们需要进行加一,那么这个时候就得到了新的使用,之后的结果是一一点零一零一一一零零,那么如果这里面最后一位舍去的是零的话,那么这个时候我们就不需要在这里面加一了, 当然在这里面我们进行了右移的操作,如果还有接码的话,我们记得接码要加一哦。虽然说这个零舍一入法可以大大的提升尾数规格化的经营度,但是这个零舍一入法可能会溢出,那么什么时候可能发生这个溢出的情况呢?我们来看下面这个例子, 假设这个 s 的补码呢等于零一点八个一,那么在这里面符号位是不一样的,所以我们需要进行使用的操作,而根据这个使用的操作呢,需要舍去末位的一,所以这个时候我们需要加一,那么加 之后得到的结果是零一点八个零,那么这个时候得到的结果双符号位还是不一致的,所以这个时候我们还是要进行一次右移的操作,这一次舍去的这个尾数呢是零, 所以我们不需要进行下一次的这个加一的操作了,那么这个时候我们就得到了 s 补码,两次右移之后的结果是零,零点一接七个零,那么这个呢也是满足这个规格化的这个要求的, 在这里面需要记住的是我们进行了两侧的右移,所以记得肩膀要加二,那么这一个呢就是零舍一入法使用的操作。了解了这个使用操作之后呢,我们接下来再来进行 溢出判断的学习,那么对于溢出判断,前面我们在学习定点数的加减法的时候,我们有一个判断方法,就是使用双符号位判断法,当定点数运用上,结果双符号位不一致的时候,我们就称之 为易出。这个方法在伏点数据上的时候并不适用,因为在伏点数运算里面,尾数的双符号位不一致并不算溢出,因为当伏点数尾数双符号位不一致的时候,可以进行右移来把这个双符号位不一致变成一致。那么在符点数据上里面怎么样进行这个溢出的判断呢? 其实在伏点数里面,主要是通过接码的双符号位来判断是否溢出的,如果尾数规格化之后,接码的双符号位还是不一致的话,我们就称之为辅点数的溢出。 了解了这里面溢出判断的方法之后呢,我们最后通过一个例子来理解这里面伏点数加减法运算的整个过程, 假设这个 s 呢等于这一个数, y 呢等于这一个数,在这里面编码,我们使用四位存储,尾数使用八位存储,那么这个时候计算 x 加 y 的结果。首先我们来进行第一步对接的操作,对接呢,因为这里面 s 的接码是一一零一,而 y 的接码是一一零零, 两个数的接码是不一致的,所以我们需要进行对接的操作。对接的原则是小接看齐,大接以大接的为准,所以这个时候我们需要将歪的尾数又移移位来完成这个对接的操作。 那么右移呢,就需要将这个 y 的尾数进行一个舍入的操作,所以这个时候我们把最右边的零舍弃,并且在最左边加一个零,来完成这个舍入的操作。完成了第一步对接操作之后呢,我们进行第二步的操作。第二步尾数求和, 对于尾数求和,我们先求出 x 的补码以及 y 的补码,答案呢,在这里面 x 和 y 都是正的,所以这个时候 x 补码等于 x, 圆码等于 零零点一一零一零零一一,歪的补码呢等于歪的圆码等于零零点零一一一零一一一。那么这个时候进行求和运算就得到零一点零一零零一零一零, 可以看到尾数求和之后呢,他的符号位是零一,双符号位是不一致的,这个在定点预算的情况下是属于溢出的,但是在辅点运算并不算溢出, 所以我们接下来进行第三步的操作。第三步呢就是这个尾数的规格化,答案呢,在这里面进行的是右规操作,因为他的双符号位不一致, 对于这个结果零一点零一零零一零一零,我们需要进行右规操作,然后对于这个右移的操作,我们需要将这个最后一位的零给舍去掉,并且对于符号位呢补一个零。第四步进行所右的操作,因为我们舍去了一个零,所以呢 按照零舍约法的话,我们可以直接的把它舍去掉,并不需要加一,所以这个时候我们就得到结果,尾数的符号位呢是零零, 尾数的数值位是一零一零零一零一。最后呢我们进行溢出判断的操作,那么溢出判断呢,我们并不是判断尾数的符号为的,而是判断接满的符号位,那么在这里面接满的符号为为零零,所以没有发生溢出,因此呢我们就得到了 x 加 y 的结果, x 加 y 的结果呢等于零点一零一零零一零一乘以二的一一一零之方,那么这一个就是 x 加 y 的结果。最 后来回顾辅点数再加法运算的整个过程。首先进行第一步,第一步操作是这个对接的操作,对于这个对接我们有一个原则,就是小接看齐大接,那么对接的 目的是为了让尾数可以直接的进行加减法运算。对接完成之后呢,我们就进行第二步的操作,第二步是这个尾数求和,那么对于这个尾数求和呢,就和定点数的加减法操作一样的。 接着我们进行尾数的规格化操作。那么对于这个尾数的规格化,我们首先需要判断双符号位是否不一致,如果没有不一致的话,我们再来判断是否需要左移,这个是否需要左移呢?我们是需要对这个尾数的格式进行一个判断的,如果不需要左移的话,我们就可以进行最后的一处判断并且输出了, 如果我们需要左移的话,我们进行左移的操作。左移完成之后呢,我们还需要进行满足规格化的操作,如果满足规格化的话,我们进行应处判断并且输出结果,如果还不满足规格化的话,我们还需要进行下一次的尾数规格化操作。而对于这个 双符号位是否不一致,如果双符号位不一致的话,那么我们需要进行右移的操作,而右移呢需要进行舍入,这个舍入是使用零舍易入法,这个零舍易入法就是二进制的四舍五入法, 使用之后呢,我们再来判断是否满足规格化,如果满足的话我们直接进行艺术的判断,并且输出结果,否则还需要进行下一次的尾数规格化。那么这一个呢,就是辅点数加减法运算的整一个过程。好的,那么这节课的内容呢?就到这里了。