Lua和C++中的浮点数的比较

Posted ithiker

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lua和C++中的浮点数的比较相关的知识,希望对你有一定的参考价值。

文章目录


问题

最近碰到Lua语言中的浮点数比较问题,如下:

print(9007199254740991 + 0.0 == 9007199254740991)
print(9007199254740992 + 0.0 == 9007199254740992)
print(9007199254740993 + 0.0 == 9007199254740993)

上面的代码输出为:

true
true
false

将下面类似的代码改写为C/C++中的代码:

#include <iostream>
#include <iomanip>
int main() 
    
    std::cout << std::boolalpha;
    std::cout<< (9007199254740991 + 0.0 == 9007199254740991) << std::endl;
    std::cout<< (9007199254740992 + 0.0 == 9007199254740992) << std::endl;
    std::cout<< (9007199254740993 + 0.0 == 9007199254740993) << std::endl;

输出为

true
true
true

为什么Lua中第三个输出是false,为什么C++给出的输出都是true呢?本文结合IEEE-754中关于浮点数的表示机制,分析上述结果出现的原因,同时给出C/C++浮点数进行比较应该注意的事项。


一、IEEE-754浮点数表示机制

1. 表示机制

IEEE-754浮点数中涉及到32, 64, 128位浮点数表示机制,它们的原理类似, n n n位的比特串中第一位是符号位,中间是指数位,后面是小数位,以单精度、双精度,四精度如下表1:

符号位(sign/s)指数位(exponent/exp)小数位(fraction/frac)
Single precision3130->23 (8bit)22->0 (23bit)
Double precision6362->52 (11bit)51->0 (52bit)
Quadruple precision127126->112 (15bit)111->0 (112bit)
表1

下面给出了单精度和双精度浮点数的表示示意图1

图1(来源:csapp: fig:2.32)

浮点数的值由下面的公式1进行计算:
V = ( − 1 ) s × 2 E × M V = (−1)^s × 2^E × M V=(1)s×2E×M

公式1
  • 第一个符号位 s s s为0时,表示正数,为1时,表示负数
  • 接下来的 k k k-bit表示指数 E E E, e = e k − 1 . . . e 1 e 0 e=e_k-1...e_1e_0 e=ek1...e1e0, E E E e x p exp exp( k k k-bit转换为无符号整数)间接表示,但不一定等于 e e e
  • 最后的 n n n-bit表示小数 M M M, f = f n − 1 . . . f 1 f 0 f=f_n-1...f_1f_0 f=fn1...f1f0, M M M f r a c frac frac( n n n-bit转换为小数)间接表示,但不一定等于 f f f

根据 e x p exp exp的编码是否为全0,全1,以及不为全0或全1,浮点数可以分为三类:

  1. 规格化的浮点数: e x p exp exp的编码不为全0或全1
  2. 非规格化的浮点数: e x p exp exp的编码为全0
  3. 特殊值: e x p exp exp的编码为全1
    • 如果 f r a c frac frac位全为0,表示无穷大( s s s为0为正无穷大, s s s为1为负无穷大)
    • 如果 f r a c frac frac位不全为0,表示NaN(Not a Number)

下面以单精度浮点数为例给出了上面三类浮点数的表示如下图2:

图2(来源:csapp: fig:2.33)

对于规格化和非规格化的浮点数,对于公式 V = ( − 1 ) s × 2 E × M V = (−1)^s × 2^E × M V=(1)s×2E×M,其计算 M M M E E E的过程如下表2所示:

b i a s bias bias E E E E m i n E_min Emin E m a x E_max Emax M M M
规格化浮点数 2 k − 1 − 1 2^k-1-1 2k11 e − b i a s e-bias ebias 2 − 2 k − 1 2-2^k-1 22k1 2 k − 1 − 1 2^k-1-1 2k11 1 + f 1 + f 1+f
非规格化的浮点数 2 k − 1 − 1 2^k-1-1 2k11 1 − b i a s 1-bias 1bias 2 − 2 k − 1 2-2^k-1 22k1 2 − 2 k − 1 2-2^k-1 22k1 f f f
表2

2. 优缺点分析

假设8位比特串来表示一个浮点数, k = 4 , n = 3 k=4, n = 3 k=4,n=3, 那么 b i a s = 7 bias = 7 bias=7,我们有下面的表3:

描述位表示 E E E M M M V V V十进制值
无穷小1 1111 000-inf
00 0000 000-6000.0
最小非规格化数0 0000 001-6 1 8 \\frac18 81 1 512 \\frac1512 51210.001953
最大非规格化数0 0000 111-6 7 8 \\frac78 87 7 512 \\frac7512 51270.013672
最小规格化数0 0001 000-6 8 8 \\frac88 88 8 512 \\frac8512 51280.015625
最大规格化数0 1110 1117 15 8 \\frac158 815 1920 8 \\frac19208 81920240
无穷大0 1111 000inf
表3

观察上面的表格,可以发现:

  • 最大非规格化数和最小规格化数之间是平滑切换的
  • 正数和负数是对称的,由符号位决定
  • 越靠近0,表示越精确,越远离0,表示越稀疏,越粗略

3. 例子

我们来看整数12345和单精度浮点数12345.0的表示方法:
12345的二进制数0000 0000 0000 0000 0011 0000 0011 1001
对应的浮点数位表示是: 1.100000011100 1 2 ∗ 2 13 1.1 0000 0011 1001_2 * 2 ^13 1.10000001110012213, 那么由 2 k − 1 − 1 = 2 7 − 1 = 127 2^k-1-1 = 2^7 - 1 = 127 2k11=271=127 ,
e − 127 = 13 e - 127 = 13 ec++中如何输出保留三位小数的浮点数

将C ++中的浮点数舍入到一个小数位

java中的浮点数相加

解析为带有 2 位小数的浮点数

在Python中的小数点后面的浮点数中添加零

05:输出保留12位小数的浮点数