数学函数库(一)

首先来看下面这个小程序

#include <stdio.h>

int main()
{
        float var1 = 33554434.0;
        float var2 = 33554433.0;
        printf("%f\n", var1 - var2);

        return 0;
}
程序很简单,那么这段代码的输出是什么呢?遗憾地告诉大家:这段代码的输出是0.000000,这是由于float类型的精度造成的。

要想彻底弄清楚浮点数精度问题,我们必须知道浮点数的存储方式。浮点数的存储方式由RFC 754标准规定,这个标准的指定与处理器的发展过程有关。这个标准指定之前,不同计算机中浮点数的表示方法不同,这就造成了程序移植问题,后来在Intel 8087的基础上制定了RFC 754标准,这才统一了浮点数的表示方式。RFC 754标准定义了浮点数类型、各种浮点数的存储方式,并规定了一些特殊浮点数的数值。以单精度浮点数(float类型的数据)为例,单精度浮点数在存储时占用4个字节,共32比特,这32比特分成了三部分,如下图所示:

bit 31:这是标志位,0表示整数,1表示负数。

bit 23 - bit 30:这是指数位,浮点数采用科学计数法存储,这个字段存储的是浮点数转换成科学计数法后指数部分的值。

bit 0 - bit 22:这个部分保存的是浮点数转换成科学计数法后小数部分的值。

我们平常见得比较多的是十进制科学计数法,比如123 = 1.23*10^2,1234.5 = 1.2345*10^4,但是这里说的科学计数法是二进制科学计数法,表示方法与十进制表示方法类似,比如1011 = 1.011*2^3,110.011 = 1.10011*2^2。可以看到,二进制科学计数法中整数部分总是为1,因此存储数据时可以忽略整数部分只存储小数部分就可以了。因此,浮点数的bit 0 - bit 22存储的是小数部分,整数部分默认为1就可以了。还需要注意的一点是:bit 23 - bit 30不是直接存储的指数值,而是一个修正值,修正的方法是指数值+127。为什么要对指数值进行修正呢?目的是方便处理。比如0.0011 = 1.1*2^(-3),这里的指数是负值,存储时需要按照补码形式存储,而且在进行数值比较时也不方便。通过将指数+127处理后,所有的指数都变成了正数,这样处理起来就比较方便了。我们可以举几个例子,看看浮点数是怎么存储的。

2.25转换成二进制数是10.01,用科学计数法表示为1.001*2^1,因此符号位是0,指数部分为1+127=128,小数部分是001,三部分组合在一起是01000000 00010000 00000000 00000000,转换成十六进制数是0x40100000,这就是2.25的存储方式。

0.1875转换成二进制数是0.0011,用科学计数法表示为1.1*2^(-3),因此符号位是0,指数部分是-3+127=124,小数部分是1,三部分组合在一起是00111110 01000000 00000000 00000000,转换成十六进制数是0x3e400000,这就是0.1875的存储方式。

我们可以通过下面的程序打印出一个浮点数存储时的数值:

typedef union
{
  float value;
  unsigned int word;
} float_type;

int main()
{
        float_type var;
        var.value = 2.25;
        printf("value = 0x%x\n", var.word);
        return 0;
}
由于float类型和unsigned int类型都占用四个字节,因此我们定义一个union,直接赋值然后按照%x格式打印就可以了。

我们知道,实数的数量是无穷的,但是单精度浮点数只有32比特,因此只能表示有限的实数,并不是每个实数都可以用浮点数精确表示。我们可以看下面这段代码:

int main()
{
        float var = 2.2;
        printf("var = %.25f\n", var);
        return 0;
}

程序的输出结果是var = 2.2000000476837158203125000,这就是因为浮点数无法精确表示2.2,只好找了与2.2最接近的一个数(2.2000000476837158203125000)表示2.2。浮点数的精度与浮点数的绝对值的大小有关,绝对值越小约精确,绝对值越大精确度越差,文章开头提到的例子就是由于浮点数精度造成的。当数据达到千万数量级时,单精度浮点类型已经没有办法区分出相邻的两个自然数了。虽然代码中我们设置的var1和var2数值不同,但是由于精度问题,计算机存储数据时var1和var2的值完全相同,都是0x4c000000(这个十六进制数表示的数字是33554432.0),因此var1-var2的结果就是0了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值