Oracle中的数字精度



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

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

为什么?我是不是错过了一些基本的浮点数学?

文档还说明:

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数据类型,因为它们更稳健

链接的文档页面显示

FLOAT值在内部表示为NUMBER

可能是二进制位数被四舍五入为十进制位数吗?

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

10000000000001 -> 4503599627370497
10000000000004 -> 4503599627370500

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

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

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

  1. 二进制精度向上取整至小数精度N
  2. 该数字在数学上四舍五入到第一个N个有效位置(例如,对于N=2:101变为100,106变为110(
  3. 该数字以number格式存储,具有步骤2中的前N个有效数字,并按原始数字的比例缩放(例如,结果具有相同的长度,但有N个有效位数(
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<gt;小提琴在这里。

如您所见,对于精度1-3,您只能存储一位数字,对于4-6,只能存储两位数字,等等。此外,您可以观察到,小数位数每两位(dumpscale中的前两个字节(就会发生变化。

因此,存储是:两个字节用于缩放,最多20个字节用于精度,其中每个字节包含0到99的数字。但我不知道他们在哪里丢失了精度为38的两位数(19*2,应该是20(。

最新更新