大家好,我是老六。
在数据开发中,对于需要精确保留的字段我们一般都会用decimal类型来进行存储,老六通过源码研究了decimal乘法精度和标度计算原理,我们一起来看下decimal乘法精度和标度到底计算的吧。
一、使用
Hive中的decimal类型与Java的Big Decimal格式相同。它用于表示不变的任意精度。语法和示例如:decimal(prec,scale)
这里我们对decimal类型做两点说明:
1)decimal(9,8)代表最多9位数字,后8位是小数。此时也就是说,小数点前最多有1位数字,如果超过一位则会变成null。
2)decimal(最大精度为38位,不指定小数位数的话,将会四舍五入到整数位,所以在定义的时候一定要定义精度,如(10,2))。
二、案例
1、正常案例
SQL:select cast(100.1 as decimal(4,1))*cast(1.1 as decimal(2,1));
结果:
执行计划: explain SELECT cast(100.1 as decimal(4,1))*cast(1.1 as decimal(2,1));
STAGE DEPENDENCIES:
Stage-0 is a root stage
STAGE PLANS:
Stage: Stage-0
Fetch Operator
limit: -1
Processor Tree:
TableScan
alias: _dummy_table
Row Limit Per Split: 1
Statistics: Num rows: 1 Data size: 1 Basic stats: COMPLETE Column stats: COMPLETE
Select Operator
expressions: 110.11 (type: decimal(7,2))
outputColumnNames: _col0
Statistics: Num rows: 1 Data size: 112 Basic stats: COMPLETE Column stats: COMPLETE
ListSink
三、源码
1.2.1版本的GenericUDFOPMultiply 类的关键代码
public class GenericUDFOPMultiply extends GenericUDFBaseNumeric {
.....
@Override
protected DecimalTypeInfo deriveResultDecimalTypeInfo(int prec1, int scale1, int prec2, int scale2) {
int scale = Math.min(HiveDecimal.MAX_SCALE, scale1 + scale2 );
int prec = Math.min(HiveDecimal.MAX_PRECISION, prec1 + prec2 + 1);
return TypeInfoFactory.getDecimalTypeInfo(prec, scale);
}
}
其中 HiveDecimal.MAX_SCALE 和 HiveDecimal.MAX_PRECISION 的值都是38。
从上面的关键代码中可以看到,在1.2.1中,没有重新校准精度的地方,而是使用简单粗暴的方式,各自计算precision和scale的精度,这就会导致在真实数据很大的时候,计算出来的值的精度达不到预期,也就是会不准确。
四、总结
纸上得来终觉浅,绝知此事要躬行。对于工作学习过程中遇到的问题希望大家能够亲自实践,找到答案。
欢迎关注微信公众号