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

标签 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;
}

我相信这应该能够输出任何需要的东西,但如果有人能提供改进算法的建议,我们将不胜感激。

最佳答案

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

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

关于c++ - 将 Oracle 的数字转换为字节,然后转换为值 + 指数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57722874/

相关文章:

c++ - 未处理的异常 : Access violation reading location

oracle - 在 Swift 中连接到 Oracle 数据库

Haskell:两个整数列表之间的匹配次数?

c++ - C++中的VTable是什么时候创建的?

c++ - CMake 错误 : "add_subdirectory not given a binary directory"

java - 垃圾收集与手动内存管理

c# - DbProviderFactories 中未列出的 Oracle 数据提供程序 (ODP.NET)

sql - 创建在插入时引发异常的触发器

Java - 在特定时间内从一个数字中减去32

python - 处理非常大的数字