问题来源
高精度计算是计算机工程实践中非常重要的内容,在涉及到精确计算的项目中,思考过数据库的设计。因而比较好奇MYSQL中是如何实现对decimal的支持的。本文通过源码阅读,分析理解decimal的存储及各种运算转化。参考源代码:
https://github.com/google/mysql/blob/master/include/decimal.h
https://github.com/twitter-forks/mysql/blob/master/strings/decimal.c
基础准备
首先,在头文件decimal.h中定义了基本结构体和类型:
typedef int32 decimal_digit_t;
MYSQL采取4字节为一组来存储高精度小数,可以存储9位十进制数字。不足9位部分仍然使用4字节存储。
typedef struct st_decimal_t {
int intg, frac, len;
my_bool sign;
decimal_digit_t *buf;
} decimal_t;
其中,各个字段含义如下:
intg: 整数,十进制整数部分位数
frac: 整数,十进制小数部分位数
sign: 布尔,false表示正数,true表示负数
buf: int32类型数组,每个int32存储9位十进制数字
len: 数组buf的长度
还有一些其他的宏定义如下:
typedef decimal_digit_t dec1;
typedef longlong dec2;
#define DIG_PER_DEC1 9
#define DIG_MASK 100000000
#define DIG_BASE 1000000000
#define DIG_MAX (DIG_BASE-1)
#define DIG_BASE2 ((dec2)DIG_BASE * (dec2)DIG_BASE)
#define ROUND_UP(X) (((X)+DIG_PER_DEC1-1)/DIG_PER_DEC1)
static const dec1 powers10[DIG_PER_DEC1+1]={
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
static const int dig2bytes[DIG_PER_DEC1+1]={
0, 1, 1, 2, 2, 3, 3, 4, 4, 4};
static const dec1 frac_max[DIG_PER_DEC1-1]={
900000000, 990000000, 999000000,
999900000, 999990000, 999999000,
999999900, 999999990 };
存储转化函数decimal2bin
该函数将decimal_t类型的结构体转化为二进制形式。下面结合源代码说明,为了便于理解,将整个函数按照功能顺序拆分成几个模块分析。
思路说明
前文说过,decimal是采用4字节来存储9位整数的,该函数主要围绕着该思路进行。对于任意一个小数,可以进行如下拆分:
不足9位部分|9位整数(重复多次)|小数点|9位小数(重复多次)|不足9位部分
函数声明
int decimal