hive源码之decimal乘法精度和标度计算

本文探讨了在数据开发中使用decimal类型存储重要数据的原因,并详细解释了Hive中decimal类型的使用规则,如decimal(9,8)表示最多9位数字,其中8位是小数。通过案例展示了decimal乘法操作,分析了1.2.1版本Hive的乘法源码,指出其简单相加精度和标度可能导致精度丢失的问题。建议在实践中对精度问题保持警惕,亲自验证并解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大家好,我是老六。 

在数据开发中,对于需要精确保留的字段我们一般都会用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的精度,这就会导致在真实数据很大的时候,计算出来的值的精度达不到预期,也就是会不准确。

四、总结

纸上得来终觉浅,绝知此事要躬行。对于工作学习过程中遇到的问题希望大家能够亲自实践,找到答案。


欢迎关注微信公众号

### Hive MySQL 中 `decimal` 类型的精度标度 #### 精度 (Precision) 精度是指十进制数值中小数点前后可以存储的最大位数。对于 `decimal(precision, scale)` 来说,precision 参数定义了整个数字允许的最大有效位数。 例如,在 `decimal(5, 2)` 中,总共有 5 位有效数字,其中小数部分占用了两位[^1]。 #### 标度 (Scale) 标度指定了小数点右侧的有效位数。继续上面的例子 `decimal(5, 2)`,这意味着最多有两位位于小数点之后。因此,该字段能够保存像 `-999.99` 到 `999.99` 这样的值(不考虑正负号)。当未指定 precision 或者 scale 的时候,默认情况下会采用不同的设置;比如在 MySQL 中,默认为 `decimal(10, 0)`[^2]。 #### 使用方法 - **声明**: 定义一个具有特定精度标度的列时,需遵循如下格式: ```sql column_name DECIMAL(precision, scale) ``` - **插入数据**: 插入到此类字段的数据应符合所设定的精度标度约束条件。如果尝试存入超出这些限制的数据,则可能会引发错误或自动截断处理取决于数据库系统的配置。 - **运算**: 当执行算术操作时,结果也会受到参与计算的操作数各自的精度标度的影响。特别是在乘除法过程中,可能需要额外注意最终结果的小数位管理以避免不必要的损失准确性。 #### 示例代码展示如何创建表并插入带有不同精度标度decimal 值: ```sql CREATE TABLE example ( id INT PRIMARY KEY, amount DECIMAL(7, 3), price DECIMAL(6, 2) ); INSERT INTO example VALUES (1, 12345.678, 987.65); ``` 在这个例子中,amount 字段被设计成能容纳最大七位整数加上三位小数的形式,而 price 只支持六位整数加两位小数。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值