python判断浮点数是否相等_浮点数比较:判断两个浮点数是否相等(近似)

上大学时看林锐博士的《高质量C/C++编程指南》,里面提到了浮点数比较方法,现在回过头来看有点问题。

下面是我从原文摘抄的

4.3.3 浮点变量与零值比较

【规则4-3-3】不可将浮点变量用“==”或“!=”与任何数字比较。

千万要留意,无论是float还是double类型的变量,都有精度限制。所以一定要避免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。

假设浮点变量的名字为x,应当将

if (x == 0.0) // 隐含错误的比较

转化为

if ((x>=-EPSINON) && (x<=EPSINON))

其中EPSINON是允许的误差(即精度)。

浮点数系统不能精确表示数轴上的每一个点,只能表示部分的点,并且不是均匀的,越接近0就越稠密,越远离0就越稀疏。

既然不能精确的表示,肯定会出现误差,比如

double a = 1.6;

double b = 0.3;

double c = a + b;

double d = 1.9;

c == d; // false

1.6 + 0.3 竟然不等于 1.9,这是这么回事?

因为精度的误差,运算出来的c 和 d 是数轴上浮点数点的集合里 相邻的两个点,而不是一个点。

问我怎么知道c和d相邻的?通过printf %a 打印出的浮点数二进制表示法可以证明

printf("%a\n", c); // 0x1.e666666666667p+0

printf("%a\n", d); // 0x1.e666666666666p+0

林锐博士的方法是定义一个精度,在精度范围内就认为是相等的。

这个办法表面上看是可以的,但是,不要忘了我前面提到的 “浮点数系统不能精确表示数轴上的每一个点,只能表示部分的点,并且不是均匀的,越接近0就越稠密,越远离0就越稀疏。”

上面的代码略加改动

double w = 1ULL<<63;

double a = 1.6 * w;

double b = 0.3 * w;

double c = a + b;

double d = 1.9 * w;

c == d; // false

c - d; // = 2048

printf("%a\n", c); // 0x1.e666666666667p+63

printf("%a\n", d); // 0x1.e666666666666p+63

现在,c和d仍然是数轴上相邻的两个点,但是它俩相差2048, 如果采用林锐博士的方法就很难决定 EPSINON 的取值到底是多少合适。

我变通了一下比较浮点数相等(近似)的方法:

1)先通过常规方法比较,如果相等则返回相等(这样解决了 +0 -0 比较的问题)

2) 如果常规方法不等,那么当符号位、阶码、最后一位以外的尾数都相同时,就认为相等

#include

static inline int is_float64_eq(double x, double y) {

return x == y || (*(uint64_t*)&x ^ *(uint64_t*)&y) == 1;

}

如果想把相邻的数改成相邻的几个数,微调 == 1 为 <4 或者 <8 ... 即可

如果考虑周全一点,考虑到有NaN时总是返回false,那么代码需要这样改一改

#include

static inline int is_float64_eq(double x, double y) {

return x == y || (

(*(uint64_t*)&x ^ *(uint64_t*)&y) == 1 &&

(*(uint64_t*)&x & 0x7ff0000000000000) != 0x7ff0000000000000);

}

另外需要特别说明的是:is_float64_eq 适用于比较非零的两个浮点数

所以 is_float64_eq (x, y) 的效果 会比 is_float64_eq(x - y, 0) 效果好

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值