IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Oracle数据类型Number的解析实现

    春秋十二月发表于 2020-05-08 04:23:00
    love 0
    存储格式
       Oracle Number数据类型是变长的,占0~22字节,不像编程语言中的2/4字节整数或4/8字节浮点数,关于它的存储格式与解析,DSI上有详细的描述,如下所示
                 
       
       符号位/指数字节描述如下
                
       
       数字字节描述如下
                
       
       正数或零值的计算
                
       
       负数值的计算
                

    解析实现
       由于Oracle Number的精度高达38位,远超出了基本定长整数或浮点数表达的数值范围,因此解析实际上是大整数/实数的四则运算,为避免造轮子,本文使用了GMP开源库(https://gmplib.org/),用于任意精度的算术运算,操作有符号整数、有理数和浮点数,除了在GMP机器上运行的可用内存所暗示的精度之外,对精度没有实际的限制。解析实现的核心函数是orcl_raw2number
     1 #include <stdio.h>
     2 #include <assert.h>
     3 #include <gmp.h>
     4 
     5 #define MAX_PREC  256
     6 
     7 static mpf_t s_base100;
     8 static mpf_t s_one;
     9 
    10 static void init_mpf_globals()
    11 {
    12     mpf_init_set_ui(s_base100, 100);
    13     mpf_init_set_ui(s_one, 1);
    14 }
    15 
    16 static void clear_mpf_globals()
    17 {
    18     mpf_clear(s_base100);
    19     mpf_clear(s_one);
    20 }
    21 
    22 static void orcl_raw2number(unsigned char *data, unsigned int len, mpf_t result)
    23 {
    24     unsigned int sign = *data, digit, i;
    25     int exp = sign>=128 ? sign-193 : 62-sign;
    26     int exp_val;
    27     mpf_t tmp;
    28 
    29     mpf_init2(tmp, MAX_PREC);
    30     mpf_init2(result, MAX_PREC);
    31 
    32     if(sign & 0x80){
    33         for(i=1; i<len; ++i){
    34             digit = data[i] - 1;
    35             assert(0<=digit && digit<=99);
    36 
    37             exp_val = exp - i + 1;
    38             if(exp_val < 0){ 
    39                 mpf_pow_ui(tmp, s_base100, -exp_val);
    40                 mpf_div(tmp, s_one, tmp);    
    41             }else
    42                 mpf_pow_ui(tmp, s_base100, exp_val);
    43                                 
    44             mpf_mul_ui(tmp, tmp, digit);
    45             mpf_add(result, result, tmp);
    46         }
    47     
    48     }else{
    49         --len; //ignore the last byte
    50         for(i=1; i<len; ++i){
    51             digit = 101 - data[i];
    52             assert(0<=digit && digit<=99);
    53 
    54             exp_val = exp - i + 1;
    55             if(exp_val < 0){ 
    56                 mpf_pow_ui(tmp, s_base100, -exp_val);
    57                 mpf_div(tmp, s_one, tmp);    
    58             }else
    59                 mpf_pow_ui(tmp, s_base100, exp_val);
    60                                 
    61             mpf_mul_ui(tmp, tmp, digit);
    62             mpf_add(result, result, tmp);
    63         }
    64 
    65         mpf_neg(result, result);
    66     }
    67     
    68     mpf_clear(tmp);
    69 }

    测试用例
       测试了123456.789、-123456.789、Oracle Number实际最大最小值、Oracle Number理论最大最小值
     1 int main(int argc, char *argv[])
     2 {
     3     int n = 19;
     4     char buf[256];
     5     mpf_t r;
     6 
     7     init_mpf_globals();
     8 
     9     //123456.789
    10     unsigned char data[] = {0xc3,0xd,0x23,0x39,0x4f,0x5b};    
    11     orcl_raw2number(data, sizeof(data), r);
    12     gmp_snprintf(buf, sizeof(buf), "%Ff\n\t%.*Ff(%d digits)", r, n, r, n);
    13     printf("result: %s\n", buf);
    14     printf("\t"); mpf_out_str(NULL, 10, 0, r); printf("\n");
    15     mpf_clear(r);
    16 
    17     //-123456.789
    18     unsigned char data2[] = {0x3c,0x59,0x43,0x2d,0x17,0xb,0x66};
    19     orcl_raw2number(data2, sizeof(data2), r);
    20     gmp_snprintf(buf, sizeof(buf), "%Ff\n\t%.*Ff(%d digits)", r, n, r, n);
    21     printf("result: %s\n", buf);
    22     printf("\t"); mpf_out_str(NULL, 10, 0, r); printf("\n");
    23     mpf_clear(r);
    24 
    25     //0
    26     unsigned char zero[] = {0x80};
    27     orcl_raw2number(zero, sizeof(zero), r);
    28     gmp_snprintf(buf, sizeof(buf), "%Ff\n\t%.*Ff(%d digits)", r, n, r, n);
    29     printf("result: %s\n", buf);
    30     printf("\t"); mpf_out_str(NULL, 10, 0, r); printf("\n");
    31     mpf_clear(r);
    32 
    33     //test actual max value:99999(the number of 9 is 38)
    34     unsigned char max_data[] = {0xd3,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64};
    35     orcl_raw2number(max_data, sizeof(max_data), r);
    36     gmp_snprintf(buf, sizeof(buf), "%Ff\n\t%.*Ff(%d digits)", r, n, r, n);
    37     printf("result: %s\n", buf);
    38     printf("\t"); mpf_out_str(NULL, 10, 0, r); printf("\n");
    39     mpf_clear(r);
    40 
    41     //test actual min value:-99999(the number of 9 is 38)
    42     unsigned char min_data[] = {0x2c,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x66};    
    43     orcl_raw2number(min_data, sizeof(min_data), r);
    44     gmp_snprintf(buf, sizeof(buf), "%Ff\n\t%.*Ff(%d digits)", r, n, r, n);
    45     printf("result: %s\n", buf);
    46     printf("\t"); mpf_out_str(NULL, 10, 0, r); printf("\n");
    47     mpf_clear(r);
    48 
    49     clear_mpf_globals();
    50 
    51     //test max oracle number value
    52     mpf_init2(r, 256);
    53 
    54     mpf_set_str(r, "1e125", 10);
    55     mpf_out_str(NULL, 10, 0, r); printf("\n");
    56     gmp_printf("%Ff\n", r);
    57     
    58     //test min oracle number value
    59     mpf_set_str(r, "-1e125", 10);
    60     mpf_out_str(NULL, 10, 0, r); printf("\n");
    61     gmp_printf("%Ff\n", r);
    62 
    63     mpf_clear(r);
    64 
    65     return 0;
    66 }
       输出如下
       

    春秋十二月 2020-05-08 12:23 发表评论


沪ICP备19023445号-2号
友情链接