精华内容
下载资源
问答
  • C语言浮点数整数转换的分析

    万次阅读 2011-04-21 21:10:00
    这里以C语言浮点数为例,在IEEE浮点标准下,整数转换为浮点数的过程做一些直观的分析和具体的实现,IEEE浮点数的标准细节详见(http://en.wikipedia.org/wiki/IEEE_754-2008)。 在C语言中,使用float和double...

     

            这里以C语言的浮点数为例,在IEEE浮点标准下,整数转换为浮点数的过程做一些直观的分析和具体的实现,IEEE浮点数的标准细节详见(http://en.wikipedia.org/wiki/IEEE_754-2008)。

     

            在C语言中,使用float和double类型数据分别对应单精度和双精度的浮点格式。以float为例,由于float是32位,int在32位机器上也是32位。因此,float必然不能对所有的int进行准确的表示。实际上,在数轴上,浮点数所能表示的数呈非均匀的分布。举例如下图。


     

                                             .     . -3  .   .  .  .-2  . . .  -1..........0.........1.. . .   . 2 .  .     .      3.       .

     

     

           图中的点代表float可表示的数,可以看出,在所能表示的数中,接近0的比较密集,远离0的则步长逐渐增大。具体的步长可以根据浮点数标准算出。

          以float为例,符号位1位,阶码8位,位数23位。由IEEE浮点的标准可知,非规格化数表示那些非常接近0.0的数。这时候,阶码域全为0.因此,非规格化数的步长为

                                                             2-23×2-127 ≈ 1.4×10-45

     

          而非规格化数的范围则约等于   -1.2×10-38 到1.2×10-38  在这个范围内,float能表示的数呈均匀分布。

           而int则处在规格化数的范围内。对于规格化数,步长不均匀。步长可表示为

     

                                                                             2-23×2E

            其中,E的取值范围是-126~+127。显然,随着E增大,步长增大。因此,对于越远离0的区域,步长越大。当E =  30时,步长为128。如下程序是一个验证。

                                      

     

     

             其中,浮点数表示230次方,刚阶码值E=30,程序中a数组记录x每次加一的结果,从输出中可以看出,因为步长为128,在0-64次相加中,并没有改变a[i]的值,而在后64次相加中,a[i]则保持为230次方+128

     

                                                                       

            因此,除了不均匀的步长以外,还需要考虑的是舍入的问题。由上面的测试中可知,C语言的舍入方法中,若整数处于步长的前半,则向下舍入,否则向上舍入。而对于刚好处于中间的数,例如上图中a[64],这种与前后的可取的数距离相等,则采用向偶数舍入的原则。即取表示成float形式后,最后一位为偶数(0)的数。

            分析到此,开始实现,说白的就是实现一个 float f = float(integer)的功能。函数原型如下:

                                                                           unsigned  float_itof(int i)  

          把整数i转换为float的表示形式,然后返回对应的4个字节。函数中不能使用float类型及其运算。

          代码如下:

                           

            对输入i进行 INT_MIN 到 INT_MAX的范围测试,与float(i)的结果逐字节比较。代码正确。

     

    展开全文
  • C语言标准C89里规定了3种浮点数,float型、double型和long double型,常见的浮点型长度为float型占4个字节,double型占8个字节,long double型长度要大于等于double型,下面将以float型为例进行介绍,double型和long...

    C语言标准C89里规定了3种浮点数,float型、double型和long double型,常见的浮点型长度为float型占4个字节,double型占8个字节,long double型长度要大于等于double型,下面将以float型为例进行介绍,double型和long double型只是比float型位数长,原理是一样的 。

    float型可以表示的十进制范围是-3.402823466e38~3.402823466e38,而作为同为4个字节的定点数却只能表示-2147483648~2147483647的范围,使用同样的内存空间,浮点数却能比定点数表示大得多的范围,这是不是太神奇了?既然浮点数能表示这么大的范围,那么我们为何不使用浮点数来代替定点数呢?先不说浮点数实现起来比较复杂,有些处理器还专门配置了硬件浮点运算单元用于浮点运算,主要原因是浮点数根本就无法取代定点数,因为精度问题。鱼和熊掌不可兼得,浮点数表示了非常大的范围,但它失去了精度。

    ANSI/IEEE Std 754-1985标准

    IEEE 754是最广泛使用的二进制浮点数算术标准,被许多CPU与浮点运算器所采用。IEEE 754规定了多种表示浮点数值的方式,下面介绍32位二进制的float浮点类型。它被分为3个部分,分别是符号位S(sign bit)、指数偏差E(exponent bias)和小数部分F(fraction),这三部分都是对应二进制码的。

    36398750_1.jpg

    浮点表示的一般形式为(科学技术法规则):R=(S) * (1 + F) * 2e (R:实数       S:正负符号      F:小数部分     e:指数,不同于指数偏差)。

    符号位S:占1位,0代表浮点数是正数,1代表浮点数是负数。

    指数偏差E:占8位,范围是0~255,e = E - 127,e为正值表明转换成二进制码后,按科学计数法表达时向左移动了e位, 负值表明向右移动了e位。

    小数部分F:占23位,实际上是将浮点数转换成二进制码,再按科学计数法表达,将其小数部分存在F上,由于二进制码按科学计数法表达后,只要值不为0,整数部分就必然为1,所以可以省略整数部分。

    例如,3.75的二进制码为11.11,将该二进制码按科学计数法表达为1.111,则向左移动了1位,即e=1,E=e+127=128,F记录的便是小数部分,实际为111000...000。

    下面介绍一下小数部分转换为二进制码的方式。类似于整数的形式(如7 = 22 + 21 + 20),小数部分的转换形式为2-1、2-2、2-3、2-4......,例如0.5 = 2-1,即二进制码为0.1,0.05 = 2-5 + 2-6 + 2-9 + 2-10 + 2-13 + 2-14 +...... (可无限循环),即二进制码为0.00001100110011......。如果都以16位计,那么7的二进制码为0000000000000111,0.5的二进制码为0.1000000000000000,0.05的二进制码为0.0000110011001100。这是如何换算出来的呢?且看下面的算法便知:

    换算0.5,乘法结果初始为0.5,所有乘数为2,每次用乘法结果 * 乘数,得到新的乘法结果,结果中的整数部分被提取出来,剩余的小数部分继续参加下一次乘法运算,直到剩余小数部分为0,或者无终点(无限循环)。根据表格中的整数部分可知,二进制为0.1。

    整数部分

    乘数

    乘法结果

    剩余小数部分

    0.

    2

    0.5

    0.5

    1

    1

    0

    结束

    换算0.05,乘法结果初始为0.05,所有乘数为2,每次用乘法结果 * 乘数,得到新的乘法结果,结果中的整数部分被提取出来,剩余的小数部分继续参加下一次乘法运算,直到剩余小数部分为0,或者无终点(无限循环)。根据表格中的整数部分可知,二进制为0.00001100110011......。

    整数部分

    乘数

    乘法结果

    剩余小数部分

    0.

    2

    0.05

    0.05

    0

    2

    0.1

    0.1

    0

    2

    0.2

    0.2

    0

    2

    0.4

    0.4

    0

    2

    0.8

    0.8

    1

    2

    1.6

    0.6

    1

    2

    1.2

    0.2

    0

    2

    0.4

    0.4

    0

    2

    0.8

    0.8

    1

    2

    1.6

    0.6

    1

    1.2

    0.2

    无限循环

    例1:float型浮点数125.5转化成32位二进制浮点数。

    125.5的整数和小数部分的二进制码分别为1111101和0.1,于是125.5的二进制码为1111101.1,按科学技术法写为1.1111011*26,即向左移6位,则e=6,E=e+127=133,133的二进制码为10000101。而1.1111011把整数部分的1去掉后,剩下小数部分为1111011,之后补0至23位,构成F。所以125.5的32位二进制浮点数为:

    0  10000101  11110110000000000000000

    例2:float型浮点数0.5转化成32位二进制浮点数。

    类似的,0.5的二进制码为0.1,按科学技术法写为1.0*2-1,即向右移1位,则e=-1,则E=e+127=126,126的二进制码为01111110。而1.0把整数部分的1去掉后,剩下小数部分为0,之后补0至23位,构成F。所以0.5的32位二进制浮点数为:

    0  01111110  00000000000000000000000

    几个特殊的情形

    E=0,F=0时,表示浮点数0,此时浮点数受S影响,表现出+0和-0两种0,但数值是相等的。比如二进制数0x00000000表示+0,二进制数0x80000000表示-0。

    E=0,F不等于0时,浮点数为(S) * (F) * 2e,注意e为-126,而不是0-127=-127,而且F是0.xxx格式而不是1.xxx格式,比如0x00000001的浮点数为2-126*2-23=1.4012984643248170709237295832899e-45,而不是20-127*(1+2-23)。E从0变为1,不会产生增加2倍的关系,而是计算公式改变了(恢复正常公式)。

    E=255,F不等于0时,表示非数值,也就是说是非法数,例如0x7F800001。

    E=255,F=0时,表示无穷大的数,此时浮点数受S影响,例如0x7F800000表示正无穷大,0xFF800000表示负无穷大。做除法时,如果除以0时,结果将被记作0x7F800000。

    浮点数的精度

    从前文中可以看到,1.xxx这类浮点数中,F部分最小的是2-23,对应的十进制数为1.00000011920928955078125,可以精确表示到小数点后23位,但是一些C语言书上却说float型的有效位只有6~7位,这是为什么呢?原因在于二进制小数与十进制小数没有完全一一对应的关系,二进制小数相比十进制小数来说,是离散而不是连续的,我们来看看下面这些数字:

    二进制小数    十进制小数

    2-23       1.00000011920928955078125

    2-22       1.0000002384185791015625

    2-21       1.000000476837158203125

    2-20       1.00000095367431640625

    2-19       1.0000019073486328125

    2-18       1.000003814697265625

    这里只需要关注F,上面列出了1.xxx这类浮点数中的6个最小的二进制小数,及其对应的十进制数。可以看到使用二进制所能表示的最小小数是1.00000011920928955078125,其次是1.0000002384185791015625,这两个数之间是有间隔的,如果想用二进制小数来表示8位有效数(只算小数部分,小数点前面的1是隐藏的默认值)1.00000002、1.00000003、1.00000004......这些数是无法办到的,而7位有效数1.0000001可以用2-23来表示,1.0000002可以用2-22来表示,1.0000003可以用2-23+2-22来表示。从这个角度来看,float型所能精确表示的位数只有7位,7位之后的数虽然也是精确表示的,但却无法表示任意一个想表示的数值。

    但还是有一些例外的,比如说7位有效数1.0000006这个数就无法用F表示,这也表明二进制小数对于十进制小数来说相当于是离散的,刚好凑不出1.0000006这个数,从这点来看float型所能精确表示的位数只有6位。因此float型的有效位数是6~7位,但这个说法应该不是非常准确,准确来说应该是6位,C语言的头文件中规定也是6位。对于一个很大的数,例如1234567890,它是由于指数E系数而被放大了的,但它的有效位仍然是F所能表示的6~7位有效数字。1234567890用float表示后为1234567936,只有高7位是有效位,后3位是无效的。int型可以准确的表示1234567890,而float浮点数则只能近似的表示1234567890,精度问题决定了float型无法取代int型。

    展开全文
  • C语言浮点数

    千次阅读 2019-04-13 19:56:06
    C语言规定了3种浮点数,float型、double型和long double型,其中float型占4个字节,double型占8个字节,longdouble型长度要大于等于double型,本文档将以float型为例进行介绍,double型和long double型只是比float型...

    C语言规定了3种浮点数,float型、double型和long double型,其中float型占4个字节,double型占8个字节,longdouble型长度要大于等于double型,本文档将以float型为例进行介绍,double型和long double型只是比float型位数长,原理都是一样的。

     

    float型可以表示的范围是-3.402823466e38~3.402823466e38,而作为同为4个字节的定点数却只能表示-2147483648~2147483647的范围,使用同样的内存空间,浮点数却能比定点数表示大得多的范围,这是不是太神奇了?既然浮点数能表示这么大的范围,那么我们为何不使用浮点数来代替定点数呢?

     

    先不说浮点数实现起来比较复杂,有些处理器还专门配置了硬件浮点运算单元用于浮点运算,主要原因是浮点数根本就无法取代定点数,因为精度问题。鱼和熊掌不可兼得,浮点数表示了非常大的范围,但它失去了非常准的精度。在说明精度问题前,我们先了解一下浮点数的格式。

     

    ANSI/IEEEStd 754-1985标准

    IEEE 754是最广泛使用的二进制浮点数算术标准,被许多CPU与浮点运算器所采用。IEEE754规定了多种表示浮点数值的方式,在本文档里只介绍32bits的float浮点类型。它被分为3个部分,分别是符号位S(sign bit)、指数偏差E(exponent bias)和小数部分F(fraction)。

    其中S位占1bit,为bit31。S位为0代表浮点数是正数,S位为1代表浮点数是负数,比如说0x449A522C的S位为0,表示这是一个正数,0x849A522C的S位为1,表示这是一个负数。

    E位占8bits,为bit23~bit30。E位代表2的N次方,但需要减去127,比如说E位为87,那么E位的值为2(87-127)=9.094947017729282379150390625e-13。

    F位占23bits,为bit0~bit22。F位是小数点后面的位数,其中bit22是2-1=0.5,bit21是2-2=0.25,以此类推,bit0为2-23=0.00000011920928955078125。但F位里隐藏了一个1,也就是说F位所表示的值是1+(F位bit22~bit0所表示的数值),比如说F位是0b10100000000000000000001,只有bit22、bit20和bit0为1,那么F位的值为1+(2-1+2-3+2-23),为1.62500011920928955078125。

     

     综上所述,从二进制数换算到浮点数的公式为:。但还有几个特殊的情形:

     

    1、若E位为0并且F位也为0时表示浮点数0,此时浮点数受S位影响,表现出+0和-0两种0,但数值是相等的。比如二进制数0x00000000表示+0,二进制数0x80000000表示-0。

    2、若E位为0并且F位不为0时浮点数为,注意F位是0.xx格式而不是1.xx格式,比如0x00000001的浮点数为=7.0064923216240853546186479164496e-46,而不是。一旦E为不为0,从0变为1,不是增加2倍的关系,因为公式改变了。

    3、若E位为255并且F位不为0时表示非数值,也就是说是非法数,例如0x7F800001。

    4、 若E位为255并且F位为0时表示无穷大的数,此时浮点数受S位影响,例如0x7F800000表示正无穷大,0xFF800000表示负无穷大。当我们使用1个数除以0时,结果将被记作0x7F800000。

           

    浮点型在多个处理器间通信时,传递的数值是它的二进制数,比如说1234.5678这个浮点数的二进制数是0x449A522B,如果使用串口发送的话,就会发现串口里发送的是0x44、0x9A、0x52和0x2B这4个数(发送的顺序也可能是逆序,这与约定的字节序有关,与浮点格式无关),接收端接收到这4个数字后再组合成0x449A522B,按照IEEE 754的定义被解析成1234.5678,这样就实现浮点数通信了。如果两个处理器所使用的浮点数规则不同,则无法传递浮点数。

           

    浮点数的换算

    下面来看看浮点数与二进制数如何转换。

    1、二进制数换算成浮点数:

     

    假如在内存中有一个二进制数为0x449A522C,先将十六进制转换成二进制,如下:

    0100 0100  1001  1010 0101  0010  0010 1100

     

    按照SEF的格式分段,如下:

    0 10001001  00110100101001000101100

     

    这个数值不是特殊的情形,可以按照公式(-1)S×2E-127×(1+F)转换。S位的值为(-1)0=1,E位的值为2137-127=1024。F位的值为1+2-3+2-4+2-6+2-9+2-11+2-14+2-18+2-20+2-21= 1.205632686614990234375。最终结果为1×1024×1.205632686614990234375=1234.56787109375。

     

    其中F位比较长,使用二进制方式转换比较麻烦,也可以先转换成十六进制再计算,转换为十六进制如下:

    0011 0100  1010  0100 0101  1000

    0x3  0x4   0xA   0x4  0x5   0x8

     

    F位为23bits,需要在最后补一个0凑成24bits,共6个十六进制数。F位的值为1+3×16-1+4×16-2+10×16-3+4×16-4+5×16-5+8×16-6=1.205632686614990234375,与上面使用二进制方法得到的结果相同。

     

    2、浮点数换算成二进制数:

     

    下面我们将-987.654e30换算成二进制数。我们先不看符号位,将987.654e30归一化为整数部分为1的形式,也就是写作987.654e30=2E-127×(1+F)的标准形式,其中E=log(987.654e30)/log2+127=109.6+127,取E位的整数值为109+127=236,再求F=987.654e30/2236-127-1=0.52172193,这个小数位数保留8位就够了,已经超出了7位的精度。然后我们求小数部分的二进制数,这个转换就没啥好说的了,依次减去2的幂,从2-1一直到2-23,够减的位置1,不够减的位置0,例如,2-1为0.5,0.52172193-0.5=0.02172193,F位的bit22置1,2-2为0.25,0.02172193不够减,F位的bit21置0,2-3为0.125,0.02172193不够减,F位的bit20置0,2-4为0.0625,0.02172193不够减,F位的bit19置0……,一直算到F位的bit0,这样就得到F位的数值。

     

    如果觉得使用二进制方式转换太麻烦的话也可以使用十六进制进行转换。16-1为0.0625,0.52172193/0.0625=8.3,说明够减8个,记做0x8,0.52172193-0.0625×8=0.02172193,16-2为0.00390625,0.02172193/0.00390625=5.6,说明够减5个,加上刚才的0x8记做0x85,以此类推:

     

     

    16的-N次幂

    被减数

    十六进制数

    减后的数

    1

    0.0625

    0.52172193

    0x8

    0.02172193

    2

    0.00390625

    0.02172193

    0x85

    0.00219068

    3

    0.000244140625

    0.00219068

    0x858

    0.000237555

    4

    0.0000152587890625

    0.000237555

    0x858F

    0.0000086731640625

    5

    0.00000095367431640625

    0.0000086731640625

    0x858F9

    0.00000009009521484375

    6

    0.000000059604644775390625

    0.00000009009521484375

    0x858F91

     

     

    一直凑够23bits,也就是6个十六进制,得到0x858F91,换算成二进制如下所示:

    1000 0101  1000  1111 1001  0001

     

    由于只有23bits有效,因此需要去掉最后一个bit,二进制本着0舍1入的原则,变成

    1000 0101  1000  1111 1001  001

     

    最后需要再补上前面的S位和E位。由于是负数,S位为1。E位为236,二进制形式为1110 1100,将S、E、F位组合在一起就形成了:

    1 1110 1100  1000  0101 1000  1111  1001 001

     

    从左边最高位开始,4个一组合并成十六进制:

    1111 0110  0100  0010 1100  0111  1100 1001

     

    换算成十六进制为:

    0xF  0x6   0x4   0x2  0xC   0x7  0xC  0x9

    所以-987.654e30换算成二进制数为0xF642C7C9。

           

    浮点数的精度

     

    在前面的讲解中可以看到1.xx这个数量级的最小数是2-23,对应的十进制数值为1.00000011920928955078125,可以精确表示到小数点后23位,但有些C语言书上却说float型的有效位只有6~7位,这是为什么?

     

    这是因为二进制小数与十进制小数没有完全一一对应的关系,二进制小数对于十进制小数来说相当于是离散的而不是连续的,我们来看看下面这些数字:

    二进制小数        十进制小数

    2-23       1.00000011920928955078125

    2-22       1.0000002384185791015625

    2-21       1.000000476837158203125

    2-20       1.00000095367431640625

    2-19       1.0000019073486328125

    2-18       1.000003814697265625

     

    不看S位和E位,只看F位,上表列出了1.xx这个数量级的6个最小幂的二进制小数,对应的十进制在上表的右边,可以看到使用二进制所能表示的最小小数是1.00000011920928955078125,接下来是1.0000002384185791015625,这两个数之间是有间隔的,如果想用二进制小数来表示8位有效数(只算小数部分,小数点前面的1是隐藏的默认值)1.00000002、1.00000003、1.00000004...这些数是无法办到的,而7位有效数1.0000001可以用2-23来表示,1.0000002可以用2-22来表示,1.0000003可以用2-23+2-22来表示。从这个角度来看,float型所能精确表示的位数只有7位,7位之后的数虽然也是精确表示的,但却无法表示任意一个想表示的数值。

     

    但还是有一些例外的,比如说7位有效数1.0000006这个数就无法使用F位表示,二进制小数对于十进制小数来说相当于是离散的,刚好凑不出1.0000006这个数,从这点来看float型所能精确表示的位数只有6位。至于5位有效值的任何数都是可以使用F位相加组合出来的,即便是乘以E位的指数后也是可以准确表示出来的。

     

    因此float型的有效位数是6~7位,但这个说法应该不是非常准确,准确来说应该是6位,C语言的头文件中规定也是6位。

     

    对于一个很大的数,比如说1234567890,它是F位乘上E位的系数被放大了的,但它的有效位仍然是F位所能表示的6位有效数字。1234567890对应的二进制数是0x4E932C06,其中F位的数值为1.1497809886932373046875,E位的数值为230=1073741824,1073741824×1.1497809886932373046875=1234567936,对比1234567890,也只有高7位是有效位,后3位是无效的。int型定点数可以准确的表示1234567890,而float浮点数则只能近似的表示1234567890,精度问题决定了float型根本无法取代int型。

           

    浮点数的比较

     

    float型的有效位数是6位,那么我们在用float型运算时就要注意了,来看下面这段程序:

     

    [cpp] view plaincopyprint?

    1. #include <stdio.h>  
    2.    
    3. int main(void)  
    4. {  
    5.    float a = 9.87654321;  
    6.    float b = 9.87654322;  
    7.      
    8.    if(a > b)  
    9.     {  
    10.        printf("a > b\n");  
    11.     }  
    12.    else if(a == b)  
    13.     {  
    14.        printf("a == b\n");  
    15.     }  
    16.    else  
    17.     {  
    18.        printf("a < b\n");  
    19.        }  
    20.    
    21.    return 0;  
    22. }  


     

     

     

    按照我们平时的经验来说这段程序应该走a < b的分支,但程序运行的结果却走了a == b的分支,原因就是float型的精度问题,float型无法区分出小数点后的第8位数,在内存中,a和b的二进制数都是0x411E0652,因此就走了a == b的分支。

     

    某些编译器在编译时会发现a和b的值超出了浮点数的精度,会产生一个告警,提示数据超过精度,但有些编译器则不会做任何提示。最可怕的是有一类编译器,调试窗口里显示的长度超出float型的精度,比如说a的值显示为9.87654321,b的值显示为9.87654322,但在运行时,硬件可不管这套,硬件认为这2个数都是0x411E0652,因此实际运行结果是a == b的分支。

     

    由于精度这个问题的限制,我们在浮点数比较时就需要加一个可接受的精度条件来做判决,比如说上面的这个问题,如果我们认为精度在0.00001就足够了,那么a - b之差的绝对值只要小于0.00001,我们就认为a和b的值是相等的,大于0.00001则认为不等,还要考虑到a - b正负等情况,因此可以将上面的程序改写为:

     

    [cpp] view plaincopyprint?

    1. #include <stdio.h>  
    2.    
    3. int main(void)  
    4. {  
    5.    float a = 9.87654321;  
    6.    float b = 9.87654322;  
    7.    
    8.    if(a - b < -0.00001)  
    9.     {  
    10.        printf("a < b\n");  
    11.     }  
    12.    else if(a - b > 0.00001)  
    13.     {  
    14.        printf("a > b\n");  
    15.     }  
    16.    else  
    17.     {  
    18.        printf("a == b\n");  
    19.        }  
    20.    
    21.    return 0;  
    22. }  

     

     

     

    例子中a和b之差的绝对值小于0.00001,因此认为a和b是相等的,运行程序,也正确的打印了a == b。

      

    为什么我们要做一个精度的限定?这是因为我们在应用中的精度往往要低于硬件的6位精度。

     

    浮点数的加减

     二进制小数与十进制小数之间不存在一一对应的关系,因此某些十进制很整的加减法小数运算由二进制小数来实现就表现出了不整的情况,来看下面的例子:

     

    [cpp] view plaincopyprint?

    1. #include <stdio.h>  
    2.    
    3. int main(void)  
    4. {  
    5.    float a = 10.2;  
    6.    float b = 9;  
    7.    float c;  
    8.    
    9.     c= a - b;  
    10.    
    11.        printf("%f\n", c);  
    12.    
    13.    return 0;  
    14. }  

     

     

     

    如果用十进制计算的话变量c应该为1.2,在Visual C++ 2010环境下实验输出为1.200000,但实际上c变量的值是1.1999998,只不过是在输出时被四舍五入为1.200000罢了。在内存中c变量的二进制数是0x3F999998,它对应的浮点数是0.19999980926513671875。如果我们将printf函数%f的格式改为%.7f格式,就会看到c变量输出的值是1.1999998。

     

    两个数量级相差很大的数做加减运算时,数值小的浮点数会受精度限制而被忽略,看下面的例子:

     

    [cpp] view plaincopyprint?

    1. #include <stdio.h>  
    2.    
    3. int main(void)  
    4. {  
    5.    float a = 987654321;  
    6.    float b = 987.654322;  
    7.    float c;  
    8.    
    9.     c= a + b;  
    10.    
    11.    printf("%f\n", c);  
    12.    
    13.    return 0;  
    14. }  


     

     

    Visual C++ 2013上的计算结果为987655296.000000,而实际的真实值为987655308.654322,二进制值为0x4E6B79B2对应987655296,就是987655296.000000。可以看出有效值是6位,如果按四舍五入的话可以精确到8位,其中变量b贡献的有效数值只有2位。

          987654321

    +    987.654322

    --------------------------------

    987655308.654322    真实值

    987655296.000000    计算值

    987655296           二进制值,0x4E6B79B2

     

    对于这种数量级相差很大的计算,计算结果会保证高位数有效,数量级小的数相对计算结果显的太小了,不能按自身6位的精度保持,而是需要按照计算结果的6位精度保持。

     

    C语言中有关浮点数的定义

    C语言对浮点数做了一些规定,下面是摘自VisualC++ 2013头文件float.h中有关float型的定义,如下:

     

    [cpp] view plaincopyprint?

    1. #define FLT_DIG         6                       /* # of decimal digitsof precision */  
    2. #define FLT_EPSILON     1.192092896e-07F        /* smallest such that 1.0+FLT_EPSILON!= 1.0 */  
    3. #define FLT_GUARD       0  
    4. #define FLT_MANT_DIG    24                      /* # of bits in mantissa*/  
    5. #define FLT_MAX         3.402823466e+38F        /* max value */  
    6. #define FLT_MAX_10_EXP  38                      /* max decimal exponent*/  
    7. #define FLT_MAX_EXP     128                     /* max binary exponent */  
    8. #define FLT_MIN         1.175494351e-38F        /* min positive value */  
    9. #define FLT_MIN_10_EXP  (-37)                   /* min decimal exponent */  
    10. #define FLT_MIN_EXP     (-125)                  /* min binary exponent */  


     

     

    其中FLT_DIG定义了float型的十进制精度,是6位,与我们上面的讨论是一致的。

    FLT_EPSILON定义了float型在1.xx数量级下的最小精度,1.xx数量级下判断浮点数是否为0可以使用这个精度。

    FLT_MANT_DIG定义了float型F位的长度。

    FLT_MAX定义了float型可表示的最大数值。

    FLT_MAX_10_EXP定义了float型十进制的最大幂。

    FLT_MAX_EXP定义了float型二进制的最大幂。

    FLT_MIN定义了float型所能表示的最小正数。

    FLT_MIN_10_EXP定义了float型十进制的最小幂。

    FLT_MIN_EXP定义了float型二进制的最小幂。

     

    float.h文件里对其它的浮点数也做了规定,有兴趣的读者可以打开float.h头文件继续研究。

    展开全文
  • C语言浮点数解惑

    千次阅读 多人点赞 2013-08-12 19:15:49
     有些C语言书上说float型的有效位数是6~7位,为什么不是6位或者7位?而是一个变化的6~7位?  浮点数在内存中是如何存放的?  float浮点数要比同为4字节的int定点数表示的范围大的多,那么是否可以使用浮点数替代...

    前言

            有些C语言书上说float型的有效位数是6~7位,为什么不是6位或者7位?而是一个变化的6~7位?
            浮点数在内存中是如何存放的?
            float浮点数要比同为4字节的int定点数表示的范围大的多,那么是否可以使用浮点数替代定点数?
            为什么float型浮点数9.87654321 > 9.87654322不成立?为何10.2 - 9的结果不是1.2,而是1.1999998?为何987654321 + 987.654322的结果不是987655308.654322?
            如何才能精确比较浮点数真实的大小?
            看完本文档,你将会得到答案!
            声明:本文来自:http://bbs.chinaunix.net/thread-3746530-1-1.html,看后很受用,整理转载。
            本文的pdf文档:http://download.csdn.net/detail/chenyiming_1990/5916393,免费下载学习使用。

    C语言浮点数

            C语言标准C89里规定了3种浮点数,float型、double型和long double型,其中float型占4个字节,double型占8个字节,long double型长度要大于等于double型,本文档将以float型为例进行介绍,double型和long double型只是比float型位数长,原理都是一样的。
            float型可以表示的范围是-3.402823466e38~3.402823466e38,而作为同为4个字节的定点数却只能表示-2147483648~2147483647的范围,使用同样的内存空间,浮点数却能比定点数表示大得多的范围,这是不是太神奇了?既然浮点数能表示这么大的范围,那么我们为何不使用浮点数来代替定点数呢?
            先不说浮点数实现起来比较复杂,有些处理器还专门配置了硬件浮点运算单元用于浮点运算,主要原因是浮点数根本就无法取代定点数,因为精度问题。鱼和熊掌不可兼得,浮点数表示了非常大的范围,但它失去了非常准的精度。在说明精度问题前,我们先了解一下浮点数的格式。
           

    ANSI/IEEE Std 754-1985标准

    IEEE 754是最广泛使用的二进制浮点数算术标准,被许多CPU与浮点运算器所采用。IEEE 754规定了多种表示浮点数值的方式,在本文档里只介绍32bits的float浮点类型。它被分为3个部分,分别是符号位S(sign bit)、指数偏差E(exponent bias)和小数部分F(fraction)。
    浮点数.JPG
    2012-05-22 19:21 上传
    下载附件(7.41 KB)

    特殊的情形

            其中S位占1bit,为bit31。S位为0代表浮点数是正数,S位为1代表浮点数是负数,比如说0x449A522C的S位为0,表示这是一个正数,0x849A522C的S位为1,表示这是一个负数。
            E位占8bits,为bit23~bit30。E位代表2的N次方,但需要减去127,比如说E位为87,那么E位的值为2(87-127)=9.094947017729282379150390625e-13。
            F位占23bits,为bit0~bit22。F位是小数点后面的位数,其中bit22是2-1=0.5,bit21是2-2=0.25,以此类推,bit0为2-23=0.00000011920928955078125。但F位里隐藏了一个1,也就是说F位所表示的值是1+(F位bit22~bit0所表示的数值),比如说F位是0b10100000000000000000001,只有bit22、bit20和bit0为1,那么F位的值为1+(2-1+2-3+2-23),为1.62500011920928955078125。
            综上所述,从二进制数换算到浮点数的公式为:(-1)S×2E-127×(1+F)。但还有几个特殊的情形:
    1. 若E位为0并且F位也为0时表示浮点数0,此时浮点数受S位影响,表现出+0和-0两种0,但数值是相等的。比如二进制数0x00000000表示+0,二进制数0x80000000表示-0。

    2. 若E位为0并且F位不为0时浮点数为(-1)S×2-126×F,注意,E位的指数是-126,而不是0-127=-127,而且F位是0.xx格式而不是1.xx格式,比如0x00000001的浮点数为
         2-126×2-23=1.4012984643248170709237295832899e-45,而不是20-121×(1+2-23)。一旦E为不为0,从0变为1,不是增加2倍的关系,因为公式改变了。

    3. 若E位为255并且F位不为0时表示非数值,也就是说是非法数,例如0x7F800001。

    4. 若E位为255并且F位为0时表示无穷大的数,此时浮点数受S位影响,例如0x7F800000表示正无穷大,0xFF800000表示负无穷大。当我们使用1个数除以0时,结果将被
        记作 0x7F800000。

           
            浮点型在多个处理器间通信时,传递的数值是它的二进制数,比如说1234.5678这个浮点数的二进制数是0x449A522B,如果使用串口发送的话,就会发现串口里发送的是0x44、0x9A、0x52和0x2B这4个数(发送的顺序也可能是逆序,这与约定的字节序有关,与浮点格式无关),接收端接收到这4个数字后再组合成0x449A522B,按照IEEE 754的定义被解析成1234.5678,这样就实现浮点数通信了。如果两个处理器所使用的浮点数规则不同,则无法传递浮点数。
           

    浮点数的换算

            下面来看看浮点数与二进制数如何转换。

          例1,二进制数换算成浮点数:

            假如在内存中有一个二进制数为0x449A522C,先将十六进制转换成二进制,如下:
    0100  0100  1001  1010  0101  0010  0010  1100
            按照SEF的格式分段,如下:
    0  10001001  00110100101001000101100
            这个数值不是特殊的情形,可以按照公式(-1)S×2E-127×(1+F)转换。S位的值为(-1)0=1,E位的值为2137-127=1024。F位的值为1+2-3+2-4+2-6+2-9+2-11+2-14+2-18+2-20+2-21= 1.205632686614990234375。最终结果为1×1024×1.205632686614990234375= 1234.56787109375。
            其中F位比较长,使用二进制方式转换比较麻烦,也可以先转换成十六进制再计算,转换为十六进制如下:
    0011  0100  1010  0100  0101  1000
    0x3   0x4   0xA   0x4   0x5   0x8
            F位为23bits,需要在最后补一个0凑成24bits,共6个十六进制数。F位的值为1+3×16-1+4×16-2+10×16-3+4×16-4+5×16-5+8×16-6=1.205632686614990234375,与上面使用二进制方法得到的结果相同。        

           例2,浮点数换算成二进制数:

            下面我们将-987.654e30换算成二进制数。我们先不看符号位,将987.654e30归一化为整数部分为1的形式,也就是写作987.654e30=2E-127×(1+F)的标准形式,其中E=log(987.654e30)/log2+127=109.6+127,取E位的整数值为109+127=236,再求F=987.654e30/2236-127-1=0.52172193,这个小数位数保留8位就够了,已经超出了7位的精度。然后我们求小数部分的二进制数,这个转换就没啥好说的了,依次减去2的幂,从2-1一直到2-23,够减的位置1,不够减的位置0,例如,2-1为0.5,0.52172193-0.5=0.02172193,F位的bit22置1,2-2为0.25,0.02172193不够减,F位的bit21置0,2-3为0.125,0.02172193不够减,F位的bit20置0,2-4为0.0625,0.02172193不够减,F位的bit19置0……,一直算到F位的bit0,这样就得到F位的数值。

            如果觉得使用二进制方式转换太麻烦的话也可以使用十六进制进行转换。16-1为0.0625,0.52172193/0.0625=8.3,说明够减8个,记做0x8,0.52172193-0.0625×8=0.02172193,16-2为0.00390625,0.02172193/0.00390625=5.6,说明够减5个,加上刚才的0x8记做0x85,以此类推:

       

            一直凑够23bits,也就是6个十六进制,得到0x858F91,换算成二进制如下所示:
                                               1000  0101  1000  1111  1001  0001
            由于只有23bits有效,因此需要去掉最后一个bit,二进制本着0舍1入的原则,变成
                                               1000  0101  1000  1111  1001  001
            最后需要再补上前面的S位和E位。由于是负数,S位为1。E位为236,二进制形式为1110 1100,将S、E、F位组合在一起就形成了:
                                               1  1110 1100  1000  0101  1000  1111  1001  001
            从左边最高位开始,4个一组合并成十六进制:
                                               1111  0110  0100  0010  1100  0111  1100  1001
            换算成十六进制为:
                                               0xF   0x6   0x4   0x2   0xC   0x7  0xC   0x9
            综上所述,-987.654e30换算成二进制数为0xF642C7C9。
           

    浮点数的精度

            在前面的讲解中可以看到1.xx这个数量级的最小数是2-23,对应的十进制数值为1.00000011920928955078125,可以精确表示到小数点后23位,但有些C语言书上却说float型的有效位只有6~7位,这是为什么?
            这是因为二进制小数与十进制小数没有完全一一对应的关系,二进制小数对于十进制小数来说相当于是离散的而不是连续的,我们来看看下面这些数字:
            不看S位和E位,只看F位,上表列出了1.xx这个数量级的6个最小幂的二进制小数,对应的十进制在上表的右边,可以看到使用二进制所能表示的最小小数是1.00000011920928955078125,接下来是1.0000002384185791015625,这两个数之间是有间隔的,如果想用二进制小数来表示8位有效数(只算小数部分,小数点前面的1是隐藏的默认值)1.00000002、1.00000003、1.00000004...这些数是无法办到的,而7位有效数1.0000001可以用2-23来表示,1.0000002可以用2-22来表示,1.0000003可以用2-23+2-22来表示。从这个角度来看,float型所能精确表示的位数只有7位,7位之后的数虽然也是精确表示的,但却无法表示任意一个想表示的数值。
            但还是有一些例外的,比如说7位有效数1.0000006这个数就无法使用F位表示,二进制小数对于十进制小数来说相当于是离散的,刚好凑不出1.0000006这个数,从这点来看float型所能精确表示的位数只有6位。至于5位有效值的任何数都是可以使用F位相加组合出来的,即便是乘以E位的指数后也是可以准确表示出来的。
            因此float型的有效位数是6~7位,但这个说法应该不是非常准确,准确来说应该是6位,C语言的头文件中规定也是6位。

           
            对于一个很大的数,比如说1234567890,它是F位乘上E位的系数被放大了的,但它的有效位仍然是F位所能表示的6位有效数字。1234567890对应的二进制数是0x4E932C06,其中F位的数值为1.1497809886932373046875,E位的数值为230=1073741824,1073741824×1.1497809886932373046875=1234567936,对比1234567890,也只有高7位是有效位,后3位是无效的。int型定点数可以准确的表示1234567890,而float浮点数则只能近似的表示1234567890,精度问题决定了float型根本无法取代int型。
           

    浮点数的比较

            从上面的讨论可以看出,float型的有效位数是6位,那么我们在用float型运算时就要注意了,来看下面这段程序:
    #include <stdio.h>
    
    int main(void)
    {
        float a = 9.87654321;
        float b = 9.87654322;
        
        if(a > b)
        {
            printf("a > b\n");
        }
        else if(a == b)
        {
            printf("a == b\n");
        }
        else
        {
            printf("a < b\n");
            }
    
        return 0;
    }
            按照我们平时的经验来说这段程序应该走a < b的分支,但程序运行的结果却走了a == b的分支,原因就是float型的精度问题,float型无法区分出小数点后的第8位数,在内存中,a和b的二进制数都是0x411E0652,因此就走了a == b的分支。
            某些编译器在编译时会发现a和b的值超出了浮点数的精度,会产生一个告警,提示数据超过精度,但有些编译器则不会做任何提示。最可怕的是有一类编译器,调试窗口里显示的长度超出float型的精度,比如说a的值显示为9.87654321,b的值显示为9.87654322,但在运行时,硬件可不管这套,硬件认为这2个数都是0x411E0652,因此实际运行结果是a == b的分支。以前就遇到过一个同学在QQ群里问一个类似的问题,在调试窗口里明明写着a是9.87654321,小于 b的9.87654322,但运行结果却是a ==b。当时我给他说了半天也没能让他明白这个问题,希望他能有机会看到这个文档,希望他这次能够明白^_^。
           
            由于精度这个问题的限制,我们在浮点数比较时就需要加一个可接受的精度条件来做判决,比如说上面的这个问题,如果我们认为精度在0.00001就足够了,那么a - b之差的绝对值只要小于0.00001,我们就认为a和b的值是相等的,大于0.00001则认为不等,还要考虑到a - b正负等情况,因此可以将上面的程序改写为:
    #include <stdio.h>
    
    int main(void)
    {
        float a = 9.87654321;
        float b = 9.87654322;
    
        if(a - b < -0.00001)
        {
            printf("a < b\n");
        }
        else if(a - b > 0.00001)
        {
            printf("a > b\n");
        }
        else
        {
            printf("a == b\n");
            }
    
        return 0;
    }
            例子中a和b之差的绝对值小于0.00001,因此认为a和b是相等的,运行程序,也正确的打印了a == b。

            也许你会觉得费了这么大的劲,最后2个程序运行的结果还是一样的,这不是画蛇添足么?硬件已经自动考虑到精度问题了,为什么我们还要去再做一个精度的限定?这是因为我们在应用中的精度往往要低于硬件的6位精度。比如说我们使用2个AD采集2V的电压,并对这2个电压值做比较,一般要求精确到0.1V即可。实际情况下AD采集出来的数值都会围绕真实值有上下的波动,比如说AD的精度是0.001V,我们采集出的电压数值就可能是2.003V、2.001V、1.999V等围绕2V波动的数值,如果我们在程序里不对精度加以限制就对这些数值做比较,就会发现几乎每次采集到的电压值都是不同的。在这种情况下我们就需要将精度设定为0.1V,将上面例子中的0.00001改为0.1,就可以正确的判断出每次采集的电压值都是相同的了。
            在实际使用AD采样时可能并不需要使用浮点数,我一般都是使用定点数来代替浮点数进行处理的,请参考另一篇案例《C语言使用定点数代替浮点数计算》。

            下面我们再看一个例子:
    #include <stdio.h>
    
    int main(void)
    {
        float a = 987654321;
        float b = 987654322;
    
        if(a - b < -0.00001)
        {
            printf("a < b\n");
        }
        else if(a - b > 0.00001)
        {
            printf("a > b\n");
        }
        else
        {
            printf("a == b\n");
            }
    
        return 0;
    }
            这个例子中的两个数都是很大的数,已经远远超过了0.00001的精度,运行结果是不是应该是a < b?但程序运行的结果依然是a == b。这是因为这个例子里的a和b并不是1.xx的数量级,我们将a和b进行归一化,都除以900000000就会发现a = 1.09739369,b = 1.097393691,只是在第9位才出现不同,因此在0.00001这个精度下,这2个数还是相等的。换个角度来看,a和b虽然是很大的数了,但F位仅能表示23bits,有效值仅有6位,a和b的大是因为E位的指数放大F位表现出来的,但有效值依然是6位。在内存中a和b的二进制数都是0x4E6B79A3。其中E位为156,2156-127=536870912,F位为0xD6F346,F位1.xx数量级的1.83964955806732177734375被E位放大了536870912倍,E位如果算7位有效精度的话能精确到0.0000001,乘以536870912已经被放大了53倍多,这就说明a和b的个位与十位已经不是有效位数了,所以程序运行的结果表现出a == b也是正常的。
            由此可见,设定一个合理的精度是需要结合浮点数的数量级的,这看起来似乎比较难,毕竟在程序运行时十分精确的跟踪每个浮点数的数量级并不容易实现,但实际应用中需要比较的浮点数往往都会有其物理含义,例如上面电压的例子,因此,根据浮点数的物理含义,浮点数的精度还是比较好确定的。当然在一些复杂的数值运算过程中可能会存在非常复杂的情况,这时浮点数的精度问题就比较棘手了,所幸我所做的都是比较简单的东西,那些复杂的情况就不讨论了,我也没能力讨论^_^。
            上面所说的都是同等数量级下的浮点数进行比较的情况,不同数量级的浮点数比较则没有这个限制,比如说1.23456789与12.23456789的比较,在E位已经足以区分大小了,因此F位的精度就没有必要再比较了。
           
    浮点数的加减
            二进制小数与十进制小数之间不存在一一对应的关系,因此某些十进制很整的加减法小数运算由二进制小数来实现就表现出了不整的情况,来看下面的例子:
    #include <stdio.h>
    
    int main(void)
    {
        float a = 10.2;
        float b = 9;
        float c;
    
        c = a - b;
    
            printf("%f\n", c);
    
        return 0;
    }
            如果用十进制计算的话变量c应该为1.2,在Visual C++ 2010环境下实验输出为1.200000,但实际上c变量的值是1.1999998,只不过是在输出时被四舍五入为1.200000罢了。在内存中c变量的二进制数是0x3F999998,它对应的浮点数是0.19999980926513671875。如果我们将printf函数%f的格式改为%.7f格式,就会看到c变量输出的值是1.1999998。
           
            两个数量级相差很大的数做加减运算时,数值小的浮点数会受精度限制而被忽略,看下面的例子:
    #include <stdio.h>
    
    int main(void)
    {
        float a = 987654321;
        float b = 987.654322;
        float c;
    
        c = a + b;
    
        printf("%f\n", c);
    
        return 0;
    }
            Visual C++ 2010上的计算结果为987655296.000000,而实际的真实值为987655308.654322,二进制值为0x4E6B79B2对应987655296,就是987655296.000000。可以看出有效值是6位,如果按四舍五入的话可以精确到8位,其中变量b贡献的有效数值只有2位。
    987654321
    +     987.654322
    ----------------
    987655308.654322    真实值
    987655296.000000    计算值
    987655296           二进制值,0x4E6B79B2
            对于这种数量级相差很大的计算,计算结果会保证高位数有效,数量级小的数相对计算结果显的太小了,不能按自身6位的精度保持,而是需要按照计算结果的6位精度保持。

    使用二进制数比较浮点数

            下面我们从另一个方向探索一下浮点数的比较问题。
            我们可以使用(-1)S×2E-127×(1+F)这个公式来计算IEEE 754标准规定的浮点数,先抛开S位和那4种特殊的规定,只看E位和F位2E-127×(1+F),我们会发现E位和F位组成的数值具有单调递增性,也就是说任意一个浮点数A掩掉S位的数值B = (A & 0x7FFFFFFF)是单调递增的,如果A1大于A2,那么B1一定大于B2,反之亦然,如果B1大于B2,那么A1也一定大于A2,这样的话我们就可以使用浮点数的二进制数来比较大小了。
            看下面程序,使用联合体将浮点数转换成二进制数再做比较:
    #include <stdio.h>
    
    typedef union float_int
    {
        float f;
        int i;
    }FLOAT_INT;
    
    int main(void)
    {
        FLOAT_INT a;
        FLOAT_INT b;
        int ca;
        int cb;
    
        a.f = 9.87654321;
        b.f = 9.87654322;
    
        ca = a.i & 0x7FFFFFFF;
        cb = b.i & 0x7FFFFFFF;
    
        if(ca > cb)
        {
            printf("a > b\n");
        }
        else if(ca == cb)
        {
            printf("a == b\n");
        }
        else
        {
            printf("a < b\n");
        }
    
        return 0;
    }
            上面的程序使用联合体使浮点型和整型共享同一个内存空间,浮点型变量.f输入浮点数,使用整型变量.i就可以获取到.f的二进制数,比较时利用.i的E位和F位就可以判断浮点数的绝对大小了,这个判决的精度为硬件所支持的精度。
           
            如果考虑到S位,情况会有些变化。S位是符号位,0正1负,与int型的符号位有一样的作用,并且都在bit31。从这点来看,不对浮点数的二进制数进行(& 0x7FFFFFFF)的操作而是直接使用浮点数的二进制数来当做int型数做比较,那么浮点数的S位则正好可以充当int型数的符号位。两个比较的浮点数都是正数的情况就不用说了,上面的例子(& 0x7FFFFFFF)已经验证了。正浮点数与负浮点数比较的情况也没有问题,浮点数和int型数的符号位是兼容的,符号位就可以直接比较出大小,比如说-9.87654321和9.87654322之间做比较,-9.87654321的bit31是1,9.87654322的bit31是0,从二进制int型数的角度来看,bit31为0是正数,bit31为1是负数,通过符号位就可以直接判断出大小。最后剩下两个负浮点数比较的情况了,这种情况存在问题,如果采用二进制int型数来比较浮点数的话,结果则正好相反,比如说-1.5和-1.25做比较,int型数是用补码表示的,对于两个负数来说,补码的二进制数值越大则补码值也越大。-1.5的补码是0xBFC00000,-1.25的补码是0xBFA00000,从二进制角度来看0xBFC00000>0xBFA00000,因此int的补码是0xBFC00000>0xBFA00000,也就是-1077936128>-1080033280,如果使用int型来判断,就会得出-1.5 > -1.25的结论,正好相反了。这样的话我们就需要对两个浮点数的符号位做一个判断,如果同为负数的话则需要将比较结果反一下,如下面程序:
    #include <stdio.h>
    
    typedef union float_int
    {
        float f;
        int i;
    }FLOAT_INT;
    
    int main(void)
    {
        FLOAT_INT a;
        FLOAT_INT b;
    
        a.f = -9.876543;
        b.f = -9.876542;
    
        if((a.i < 0) && (b.i < 0))
        {
            if(a.i < b.i)
            {
                printf("a > b\n");
            }
            else if(a.i == b.i)
            {
                printf("a == b\n");
            }
            else
            {
                printf("a < b\n");
            }
        }
        else
        {
            if(a.i > b.i)
            {
                printf("a > b\n");
            }
            else if(a.i == b.i)
            {
                printf("a == b\n");
            }
            else
            {
                printf("a < b\n");
            }
        }
    }
            如果再考虑IEEE 754标准定义的那几种特殊情况,问题变得又会复杂一些,比如说在运算过程中有±x / 0的情况出现,那么结果就是一个±无穷大的数,还有可能遇到±0等情况,这些问题在这里就不讨论,只要增加相应的条件分支就可以做出判断的。
           
            使用二进制数比较浮点数的方法可以依据硬件精度判断出浮点数的真正大小,但实际使用过程中往往不是根据硬件精度做判断的,因此最好还是使用上面所介绍的加入精度的判断方法。
           

    C语言中有关浮点数的定义

            C语言对浮点数做了一些规定,下面是摘自Visual C++ 2010头文件float.h中有关float型的定义,如下:
    #define FLT_DIG                     6                                         /* # of decimal digits of precision */
    #define FLT_EPSILON          1.192092896e-07F         /* smallest such that 1.0+FLT_EPSILON != 1.0 */
    #define FLT_GUARD             0
    #define FLT_MANT_DIG       24                                       /* # of bits in mantissa */
    #define FLT_MAX                   3.402823466e+38F        /* max value */
    #define FLT_MAX_10_EXP  38                                       /* max decimal exponent */
    #define FLT_MAX_EXP        128                                     /* max binary exponent */
    #define FLT_MIN                  1.175494351e-38F         /* min positive value */
    #define FLT_MIN_10_EXP  (-37)                                  /* min decimal exponent */
    #define FLT_MIN_EXP         (-125)                               /* min binary exponent */
            其中FLT_DIG定义了float型的十进制精度,是6位,与我们上面的讨论是一致的。
            FLT_EPSILON定义了float型在1.xx数量级下的最小精度,1.xx数量级下判断浮点数是否为0可以使用这个精度。
            FLT_GUARD不知道是啥意思--!
            FLT_MANT_DIG定义了float型F位的长度。
            FLT_MAX定义了float型可表示的最大数值。
            FLT_MAX_10_EXP定义了float型十进制的最大幂。
            FLT_MAX_EXP定义了float型二进制的最大幂。
            FLT_MIN定义了float型所能表示的最小正数。
            FLT_MIN_10_EXP定义了float型十进制的最小幂。
            FLT_MIN_EXP定义了float型二进制的最小幂。

            float.h文件里对其它的浮点数也做了规定,本文不再做介绍了。
    展开全文
  • C语言浮点数运算

    2013-04-16 19:49:00
    C语言标准C89里规定了3种浮点数,float型、double型和long double型,常见的浮点型长度为float型占4个字节,double型占8个字节,long double型长度要大于等于double型,下面将以float型为例进行介绍,double型和long...
  •  有些C语言书上说float型的有效位数是6~7位,为什么不是6位或者7位?而是一个变化的6~7位?  浮点数在内存中是如何存放的?  float浮点数要比同为4字节的int定点数表示的范围大的多,那么是否可以使用浮点数...
  • #include <stdio.h>...这是因为计算机不能保证其内部转换为二进制的浮点数的每一位都不发生数据丢失。所以在浮点数计算时,会存在误差并进行积累。 运行结果: #include <stdio.h> int m
  • C语言标准C89里规定了3种浮点数,float型、double型和long double型,常见的浮点型长度为float型占4个字节,double型占8个字节,long double型长度要大于等于double型,下面将以float型为例进行介绍,double型和long...
  • 有些C语言书上说float型的有效位数是6~7位,为什么不是6位或者7位?而是一个变化的6~7位?  浮点数在内存中是如何存放的?  float浮点数要比同为4字节的int定点数表示的范围大的多,那么是否可以使用浮点数替代...
  • 整数操作数的除法总是产生整数结果。而浮点操作数的除法将提供精确的结果。下一个示例说明了除法运算如何使用float类型的变量。示例代码#include int main(void){float wood_length = 101.0f; // In feetfloat table...
  • 最近在做项目的时候,涉及到产品价格的计算,经常会出现JS浮点数精度问题,这个问题,对于财务管理系统的开发者来说,是个非常严重的问题(涉及到钱相关的问题都是严重的问题),这里把相关的原因和问题的解决方案整理...
  • 注意两个整数相除所得结果仍是整数,而非浮点数。   问题描述:已知摄氏温度(℃)与华氏温度(℉)的转换关系是:   编写一个摄氏温度(℃)与华氏温度(℉)进行转换的程序,输入摄氏温度,...
  • c语言学习浮点数

    2019-11-24 10:40:38
    浮点数即 小数 一般用 double型双精度浮点数 %lf float数据类型 单精度浮点数 表达的是一个近似的数,到小数点后8位时误差为 1 不能用 eg: 9.99999999 %f 用float定义出来 为10.00000000 #include<stdio...
  • 1)把浮点数拆分成两部分,小数点前面的整数和小数点后面的小数。 2)分别把整数和小数部分转换为二进制,保存在字符串中。 3)输出转换后和二进制字符串。 1、整数部分转二进制字符串的方法 1)把十进数除以2,记下...
  • C语言浮点数向零舍入取整的方法

    千次阅读 2017-09-19 20:24:57
    碰到一道题。
  • 匿名用户1级2014-03-10 回答%4d为了对齐,输出整数,不足4位就右对齐。附:一、printf()函数printf()函数是格式化输出函数, 一般用于向标准输出设备按规定格式输出信息。在编写程序时经常会用到此函数。printf()函数...
  • 在浮点数的编码中,IEEE754引入了一些特殊的编码来表示正无穷大、负无穷大,因为一个正浮点数除以0则得到正无穷大,而一个负浮点数除以0则得到负无穷大;而在实数范畴内,是不能对一个负数进行开根号的,所以IEEE754...
  • 只需要对其+0.5,然后强制类型转换即可 如:a=3.6,若直接进行强制类型转换,答案是3 ...只需要对其*100,然后+0.5,再除以100.0,然后强制类型转换即可 如:b=3.146,进行(int)(b*100+0.5)/100.0,答案是3.15 ...
  • 剖析Intel IA32架构下C语言及CPU浮点数机制 Version 0.01 哈尔滨工业大学 谢煜波 (email: xieyubo@126.com 网址:http://purec.binghua.com) (QQ:13916830 哈工大紫丁香BBSID:iamxiaohan...
  • c程序中“%”只能用于整数运算的运算符吗发布时间:2020-07-29 09:37:42来源:亿速云阅读:121作者:Leah这期内容当中小编将会给大家...“%”在C语言中是取余(或余数)运算符,其表示:一个表达式的值除以另一个表...
  • )两个整数相除的结果仍为整数,舍去小数部份的值。例如, 6/4 与 6.0/4 运算的结果值 是不同的, 6/4 的值为整数 1 ,而 6.0/4 的值为实型数 1.5 。这是因为当其中一个操作数为实数 时,则整数与实数运算的结果...
  • 在一些工程领域中单单依靠整数是无法满足他们对精度的需求的,这种时候就需要用到浮点数了。今天着重来聊一聊在计算机底层,浮点数的编码方式,以及它相关值的计算方式。二进制小数在介绍浮点数之前先来看看二进制中...
  • 1.当除数或者被除数之一(只要有一个)是浮点数(或double),进行的就是浮点数除法,会把另一个除数或者被除数转换为精度更高的进行除法(例如int转为float,float转为double)结果为浮点数(或double) ...
  • C语言——整型整除,浮点数整除

    千次阅读 2020-05-09 09:54:54
    #include "stdio.h" int main() { unsigned int a; double c; a = 1; c = 1; printf("%f\r\n", a/3); printf("%f\r\n", c/3); printf("%f\r\n", 1.0/3); return 0; }
  • C语言中两个整数相除的结果

    万次阅读 2019-02-03 13:43:38
    c语言中对于除法运算符,当被除数和除数都是整数时,并不会得到一个double的浮点型的数,而是直接舍去小数部分(即向下取整) 我在做patB1026时遭遇此问题,代码改了好久测试点1都不能通过,原来在一开始的除法...
  • C语言中不同类型的数据也可以使用二元运算符进行运算,不过如果不明白其中的规律的话,编出来的程序就会有些危险了。强制类型转换自动转换类型举个例子:/*测试数据的类型转换*/#includeint main(void){float num0...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,408
精华内容 1,763
关键字:

c语言浮点数除以整数

c语言 订阅