浮点数比较大小

由于本人才疏学浅,本文难免存在遗漏之处,欢迎大家留言指正,本人将感激不尽。

  最近阅读《剑指offer》,其中有提到浮点数不能直接用 ‘==’比较,之前就有听说这个问题,但一直没有深入理解。今天,查阅了一些资料,对其进行解释,方便日后查阅。

一、前言

首先来看一个例子:

#include <iostream>
#include <cmath>
using namespace std;

int main()
{
    float a = (float)0.1;
    float b = (float)0.1;
    if(a == b)
        cout << "a == b" << endl;
    else
        cout << "a != b" << endl;

    float c = (float)0.1;
    double d = (double)0.1;
    if(c == d)
        cout << "c == d" << endl;
    else
        cout << "c != d" << endl;

    float e = (float)0.1;
    float f = (double)0.1;
    if(abs(e - f) < 0.0001)
        cout << "e == f" << endl;
    else
        cout << "e != f" << endl;

    return 0;
}

这里直接给出输出如下:

a == b
c != d
e == f

  可以看到第二次比较时,c != d,明明都是0.1,那为何不相等呢?其实主要是由于精度导致的,对于计算机来说,能够表示的精度是有限的。为了理解这个问题,首先介绍计算机中浮点数的表达方式。

二、浮点数的表示

  计算机中是如何存储和表达数字的?对于整数,情况比较简单,直接按照数学中的进制转换方法处理即可,即连续除以2取余(转化为二进制)。这并不是难点,真正的难点在于小数是如何转换为二进制码(即浮点数)的(注意区别小数和浮点数)。

  当然,从数学的角度来讲,十进制的小数可以转换为二进制小数(整数部分连续除2,小数部分连续乘2),例如125.125D=1111101.001B,但问题在于计算机根本就不认识小数点“.”,更不可能认识1111101.001B。那么计算机是如何处理小数的呢?

  历史上计算机科学家们曾提出过多种解决方案,最终获得广泛应用的是IEEE 754标准中的方案,目前最新版的标准是IEEE std 754-2008。该标准提出数字系统中的浮点数是对数学中的实数(小数)的近似,同时该标准规定表达浮点数的0、1序列被分为三部分(三个域):
在这里插入图片描述
  以32位单精度浮点数为例,其具体的转换规则是:首先把二进制小数用二进制科学计数法表示,比如上面给出的例子1111101.001=1.111101001*2^6。 符号位sign表示数的正负(0为正,1为负),故此处填0。exponent表示科学计数法的指数部分,请务必注意的是,这里所填的指数并不是前面算出来的实际指数,而是等于实际指数加上一个数(指数偏移),偏移量为2^(e-1)-1,其中e是exponent的宽度(位数)。对于32位单精度浮点数,exponent宽度为8,因此偏移量为127,所以exponent的值为133,即10000101。之后的fraction表示尾数,即科学计数法中的小数部分11110100100000000000000(共23位)。因此32位浮点数125.125D在计算机中就被表示为01000010111110100100000000000000。

对于32位单精度浮点数,sign是1位,exponent是8位(指数偏移量是127),fraction是23位。对于64位双精度浮点数,sign是1为,exponent是11位(指数偏移量是1023),fraction是52位。

用程序可以看出小数在计算机中是如何表示的(即浮点数):

#include <iostream>
#include <bitset>
using namespace std;

int main()
{
    float f = 1.25;
	float f2 = -1.25;
	double d = 1.25;
    unsigned long long ff = *(unsigned long long *)&f;
	unsigned long long ff2 = *(unsigned long long *)&f2;
	unsigned long long dd = *(unsigned long long *)&d;
    bitset<32> ffBit(ff);
	bitset<32> ffBit2(ff2);
	bitset<64> ddBit(dd);
    cout << ffBit << endl;
	cout << ffBit2 << endl;
	cout << ddBit << endl;

    return 0;
}

  输出如下:

00111111101000000000000000000000
10111111101000000000000000000000
0011111111110100000000000000000000000000000000000000000000000000

三、结论

  既然已经知道了浮点数在计算机的表示形式,那我们应该清楚计算机表达精度有限,例如小数十进制0.1D表示为二进制时为0.0001100110011001100… 在单精度和双精度表示时,其结果如下:

00111101110011001100110011001101
0011111110111001100110011001100110011001100110011001100110011010

其中单精度只能表示23位小数,双精度只能表示52位小数,将多余的位截断,因此在0.1D在单精度和双精度中表示的值不相等。

所以比较浮点数一般采取如下方式:

float f = 0.1;
float d = 0.1;

if (abs(f - d) < 0.00001)
	printf("f == d\n");
else
	printf("f != d\n");
  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值