Oracle 中的数值精度

numeric precision in Oracle

提问人:ihadanny 提问时间:1/11/2021 更新时间:9/20/2021 访问量:989

问:

我有一个带有列的表,如 Oracle 文档中所定义。 我插入了一个包含 52 个二进制数字的数字(不包括隐含的初始 1),但我看到 oracle 只需要 50 位数字即可正确存储它。这怎么可能?FLOAT

create table numeric_types2(
    f1 float(60)
);
insert into numeric_types2 SELECT BIN_TO_NUM(
    1,
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,
    0,1
                                      ) FROM DUAL;

然后:

select to_char(cast(f1 as float(49)), 'XXXXXXXXXXXXXXXXX'),
       to_char(cast(f1 as float(50)), 'XXXXXXXXXXXXXXXXX'),
       to_char(cast(f1 as float(51)), 'XXXXXXXXXXXXXXXXX')
from root.numeric_types2;

返回:

10000000000004,    10000000000001,    10000000000001

为什么?我是否遗漏了一些基本的浮点数学?

SQL Oracle 精度 浮点精度

评论


答:

1赞 aka.nice 1/11/2021 #1

链接的文档页面显示

FLOAT 值在内部表示为 NUMBER

难道二进制位数四舍五入到十进制数吗?

此外,转换为十进制,十六进制数字为:

10000000000001 -> 4503599627370497
10000000000004 -> 4503599627370500

所以在我看来,这个数字在内部被转换为十进制表示,四舍五入到 14 或 15 位十进制数字 - 497 将四舍五入到 500。

请注意,在拉丁语中,数字是以 10 为基数(digitus = finger)。

2赞 Chris Saxon 1/11/2021 #2

文档还指出

FLOAT 数据类型是 NUMBER 的子类型

所以这是一个在幕后和number

要将二进制精度转换为十进制精度,请将 n 乘以 0.30103

插入数字:

49 * 0.30103 = 14.75047
50 * 0.30103 = 15.05150
51 * 0.30103 = 15.65356

所以 和对应于 ,而 是float(50)float(51)number(16)float(49)number(15)

您可以通过获取以下值来验证这一点:dump

create table numeric_types2 (
    f1 float(60), n1 number
);
insert into numeric_types2 
with rws as (
  select BIN_TO_NUM(
    1,
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,
    0,1) n from dual
)
  select n, n from rws;

select dump ( f1 ), dump ( n1 ), 
       dump ( cast ( n1 as float(50) ) ) df50, 
       dump ( cast ( n1 as float(49) ) ) df49
from   numeric_types2;

DUMP(F1)                                 DUMP(N1)                                 DF50                                     DF49                                  
Typ=2 Len=9: 200,46,4,60,97,28,38,5,98   Typ=2 Len=9: 200,46,4,60,97,28,38,5,98   Typ=2 Len=9: 200,46,4,60,97,28,38,5,98   Typ=2 Len=8: 200,46,4,60,97,28,38,6    

请注意,

dump ( f1 ) = dump ( n1 ) = dump ( cast ( n1 as float(50) ) )

仅将数字转换为会给出不同的值。float(49)

最后,请注意,文档还包含以下建议:

Oracle FLOAT 可供您使用,但 Oracle 建议 您可以改用 BINARY_FLOAT 和 BINARY_DOUBLE 数据类型,因为它们 更健壮

评论

0赞 ihadanny 1/12/2021
谢谢!此处注释上下文:这意味着如果您使用的是 cx_Oracle Python 驱动程序,则能够保存具有 53 个二进制位(不包括第一个隐式 1)的数字的最低精度是最低精度,该数字在转换为 Python 的浮点数时丢失,并被截断为FLOAT(50)0x1.0000000000000p+53
1赞 astentx 1/12/2021 #3

如前所述,这是因为此类型下有一个实现。正如您可以使用以下查询检查的那样,二进制精度被完全忽略:number

  1. 二进制精度向上舍入到十进制精度 N。
  2. 该数字在数学上四舍五入到 N 个有效位置(例如,对于 N = 2:101 变为 100,106 变为 110)。
  3. 该数字以格式存储,具有步骤 2 中的前 N 个有效数字,并按原始数字的比例缩放(例如,结果具有相同的长度,但 N 个有效数字)。number
with a as (
  select level as l,
    cast(level as float(1)) as c,
    cast(level as float(2)) as c2,
    cast(level as float(3)) as c3,
    cast(level as float(4)) as c4,
    cast(level as float(6)) as c6,
    cast(level as float(7)) as c7
  from dual
  connect by level < 121
)
select a.*,
  dump(c, 16) as cd,
  dump(c4, 16) as c4d,
  dump(power(10, l), 16) as scale
from a

db<>小提琴在这里。

如您所见,对于精度 1-3,您只能存储一位数字,对于 4-6 - 两位数字,依此类推。您还可以观察到,该刻度每两个十进制数字(列中的前两个字节)更改一次。dumpscale

因此,存储是:两个字节用于缩放,最多 20 个字节用于精度,其中每个字节保存从 0 到 99 的数字。但我无法弄清楚他们在精度 38(19*2,应该是 20)时丢失了两位数。