将Oracle的数字转换为字节,然后转换为值+指数

时间:2019-08-30 08:04:53

标签: c++ oracle numbers exponent occi

我一直在网上寻找如何将Oracle编号转换为我可以使用的格式,但是我一直找不到能够成功提出完整算法(特别是负指数)的人。

一些信息已发布在https://gotodba.com/2015/03/24/how-are-numbers-saved-in-oracle/上,但是由于无法描述负指数解,因此必须重新翻转,因此该信息尚不完整。下面是我提供的代码,使它能够成功处理(到目前为止)。我已经测试了-10,000至999,999和0.0001至2.0002的值。

struct ValueExponent {
  int64_t value = 0;
  int32_t exponent = 0; // Powers of 10
};

int32_t CalculateExponent(const ::oracle::occi::Bytes& bytes, bool& isNegative) {
  int workingExponent = static_cast<int>(bytes.byteAt(0));
  isNegative = (workingExponent & 0x80) == 0;
  if (isNegative)
    workingExponent = ~workingExponent;
  bool isNegativeExponent = (workingExponent & 0x40) == 0;
  if (isNegativeExponent)
    workingExponent = ~workingExponent;
  return ((isNegativeExponent ? -1 : 1) * (workingExponent & 0x3f)) - (isNegativeExponent ? 1 : 0);
}

ValueExponent OracleNumberToValueExponent(const ::oracle::occi::Number& num) {
  auto bytes = num.toBytes();

  int64_t value = 0;
  bool isNegative = false;
  int32_t exponent = CalculateExponent(bytes, isNegative);

  decltype(bytes.length()) max = isNegative ? bytes.length() - 1 :  bytes.length();
  for(decltype(bytes.length()) ix = 1; ix < max; ++ix) {
    auto borig = bytes.byteAt(ix);
    int b = static_cast<int>(borig);

    b -= 1;
    if (isNegative)
      b = 100 - b;

    value = value * 100 + b;
     --exponent;
  }

  ValueExponent retval;
  retval.value    = isNegative ? -value : value;
  retval.exponent = exponent * 2; //Oracle exponents are of 100 not 10
  return retval;
}

这应该能够输出我认为需要的任何东西,但是任何对改进算法有建议的人都会感激不尽。

2 个答案:

答案 0 :(得分:0)

假设您的数字来自数据库,则可以在字符串上应用逻辑,而不必重新发明如何推导值和指数,例如:

WITH nums AS (SELECT -10000 nbr from dual UNION ALL
              SELECT -0.99 nbr FROM dual UNION ALL
              SELECT 0 nbr FROM dual UNION ALL
              SELECT 0.0001 nbr FROM dual UNION ALL
              SELECT 0.99 nbr FROM dual UNION ALL
              SELECT 2.0002 nbr FROM dual UNION ALL
              SELECT 999999 nbr FROM dual UNION ALL
              SELECT 1e23 nbr FROM dual)
SELECT nbr,
       sci_num,
       TO_NUMBER(SUBSTR(sci_num, 1, INSTR(sci_num, 'E', 1, 1) - 1)) val,
       TO_NUMBER(SUBSTR(sci_num, INSTR(sci_num, 'E', 1, 1) + 1)) EXP
FROM   (SELECT nbr,
               to_char(nbr, 'fm0D099999999999999999EEEE') sci_num
        FROM   nums);

       NBR SCI_NUM                           VAL        EXP
---------- -------------------------- ---------- ----------
    -10000 -1.0E+04                           -1          4
     -0.99 -9.9E-01                         -9.9         -1
         0 0.0E+00                             0          0
    0.0001 1.0E-04                             1         -4
      0.99 9.9E-01                           9.9         -1
    2.0002 2.0002E+00                     2.0002          0
    999999 9.99999E+05                   9.99999          5
      1E23 1.0E+23                             1         23

nums子查询只是一种生成一些要在查询中使用的测试数据的方法。)

如果您需要改为在PL / SQL中实现此逻辑,则可以执行以下操作:

DECLARE
  v_num NUMBER := -10001;
  v_val NUMBER;
  v_exp NUMBER;
  v_sci_fmt_num VARCHAR2(100);
BEGIN
  v_sci_fmt_num := to_char(v_num, 'fm0D099999999999999999EEEE');
  v_val := TO_NUMBER(SUBSTR(v_sci_fmt_num, 1, INSTR(v_sci_fmt_num, 'E', 1, 1) - 1));
  v_exp := TO_NUMBER(SUBSTR(v_sci_fmt_num, INSTR(v_sci_fmt_num, 'E', 1, 1) + 1));

  -- dummy output
  dbms_output.put_line('number: '||v_num||'; value = '||v_val||', exponent = '||v_exp);
END;
/

number: -10001; value = -1.0001, exponent = 4

答案 1 :(得分:0)

查看Oracle自己的ODPI-C项目代码dpiDataBuffer__fromOracleNumberAsText()