浮点数陷阱

川长思鸟来 2022-08-19 08:41 334阅读 0赞

今天在用Hive的时候,发现浮点数比较有个陷阱,即FLOAT类型和DOUBLE类型的比较。

如果我不刻意问的话,大家估计大部分人都认为FLOAT类型的0.2和DOUBLE类型的0.2相等。事实上不是这样的,这是后来我在《Programming Hive》中找到的答案。想知道更多细节,且听我下面细细道来。

比如,我们定义了一个FLOAT类型的变量f = 0.2,和DOUBLE类型的变量 d = 0.2(这里先不针对某种语言,类型统一先用大写)。我们都知道虽然FLOAT和DOUBLE类型不同,但是却可以相互比较,这是因为有一个从FLOAT到大范围类型DOUBLE的转换,即隐式类型转换(借用Scala的概念,更形象)。一般我们这样操作时没问题的,但是,问当FLOAT类型的变量f ,和DOUBLE类型的变量 d都被赋值为同一个数值,如0.2。那么,问题就来了,这时候的比较就失效了,变量f和变量d不相等。

原因是这样的,0.2并不能用FLOAT和DOUBLE进行准确的表示。这里的0.2是最近似于0.2的精确值,且这个精确值略大于0.2,也就是0.2后面若干个0之后还存在非零值。比如说FLOAT类型的0.2实际是0.2000001,而DOUBLE类型的0.2实际是0.200000000001,这是因为一个8字节的DOUBLE值具有更多的小数位。那么,当FLOAT类型的0.2转换成DOUBLE类型的0.2时,就变成了0.200000100000。这样FLOAT类型的0.2,即0.200000100000和DOUBLE类型的0.200000000001并不相等,且前者大于后者。但是,如果你把FLOAT类型的0.2和DOUBLE类型的0.2相减时,得到的结果却不是拿0.200000100000和0.200000000001做差(因语言而异,C语言的结果类似于0.000000,Java和Scala的结果是一个-9次方的错误值)。

大家可能觉得这是个BUG。其实,所有使用IEEE标准进行浮点数编码的系统中都存在这个问题。这个问题几乎影响了现在计算机中所使用的各种各样编程语言编写的软件。所以,它应该是个陷阱。更多详细的介绍可以参考Oracle文档

规避这个问题的方法也很简单:

  1. 牺牲存储,所有的浮点数都定义成DOUBLE类型。
  2. 在和FLOAT类型比较前,把浮点数转换成FLOAT类型(可能会导致失真)。

发表评论

表情:
评论列表 (有 0 条评论,334人围观)

还没有评论,来说两句吧...

相关阅读

    相关 详解点数

    目录 1.什么是浮点数 2.举个例子 2.1.十进制转二进制 2.2.规范尾数位 2.3.计算指数位 2.4.拼接结果 3.精度丢失 4.JAVA中的浮点数思考

    相关 点数运算

    今天学习了浮点数运算(加减乘除)。浮点数运算主要包括两部分:指数运算和尾数运算。在IEEE754标准下,指数运算就是阶码的运算,类似于无符号数运算。尾数运算是原码运算。之前一直

    相关 点数陷阱

    今天在用Hive的时候,发现浮点数比较有个陷阱,即FLOAT类型和DOUBLE类型的比较。 如果我不刻意问的话,大家估计大部分人都认为FLOAT类型的0.2和DOUBLE类型

    相关 C语言点数

      浮点数的概念        浮点数也称小数或实数。例如,0.0、75.0、4.023、0.27、-937.198 都是合法的小数。这是常见的小数的表现形式,称为十进