Hive查询语句中关于浮点数的比较
/*
本文内容来自O'REILLY系列的《Programming Hive》的中文译本《Hive编程指南》的6.2.2节,我看到这里感觉十分有趣,故分享出来。
*/
在hive中,浮点数比较的一个常见陷阱出现在float和double类型进行比较的时候。考虑下面的查询语句,该语句将返回员工姓名、工资和联邦税率,过滤条件是薪水扣税超过0.2。
select name,salary,deductions['Federal Taxes'] from employees where deductions['Federal Taxes'] >0.2;
结果如下
Tywin Lannister 20000.0 0.2
Aegon Targaryen 25000.0 0.2
Robert Baratheon 15000.0 0.3
Jon Snow 10000.0 0.3
该书作者此时发出了萌萌的疑问:桥豆麻袋!为什么“deductions['Federal Taxes'] =0.2”的记录也被输出了?
然后是作者的自问自答:这个确实是hive的bug,但这也是现代计算机中所有编程语言编写的软件共同面对的问题。当用户直接写出一个浮点数0.2时,hive会将该数保存为double类型。但是之前定义deductions这个map的值的类型是float型的。因此在where比较时,hive会将税率隐式转换为double型后再进行比较。
而这样比较是不行的。数字0.2不能用float或double准确的表示,因为0.2用float型保存时具体数值是0.2000001,而用double型保存时具体数值是0.200000000001。当表中的float字段通过hive转换为double型时,其精确数值是0.200000100000,明显可以看出比double型的0.2要大。这就是为什么查询结果像是使用了>=而不是>。
这个问题是所有使用IEEE标准进行浮点数编码的系统中存在的一个普遍问题。在实际生产中,各大厂商八仙过海各显神通,提出了多种多样的解决方法。在hive中有两种规避这个方法的方法。
(1)如果是从textfile文本文件中读取数据的话,即hive从数据文件中读取字符串“0.2”并按照表模式将其转换为一个数字。那么我们可以在定义表模式的时候就将此字段规定为double型。不过这种方法会增加查询时需要的内存,并且如果存储格式是二进制文件,也不能简单的进行这样的变换。
(2)显式类型转换。在比较的时候使用cast关键字将double型转换为float型。
作者在此时又皮了一下,以下是书中原文:
/*
实际上,还有第三种解决方案,即:和钱相关的都避免使用浮点数。
*/
总结:涉及到浮点数比较时,需要保持极端谨慎的态度,要避免任何从窄类型隐式转换到广泛类型的操作。
hear me roar!