Oracle 中的数字精度
numeric precision in Oracle
我有一个 table 和 FLOAT
列,如 Oracle docs 中所定义。
我插入一个有 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;
return:
10000000000004, 10000000000001, 10000000000001
这是为什么?我错过了一些基本的浮点数学吗?
链接的文档页面说
A FLOAT value is represented internally as NUMBER
莫非二进制位数四舍五入到十进制位数?
另外,换算成十进制,十六进制数为:
10000000000001 -> 4503599627370497
10000000000004 -> 4503599627370500
所以在我看来,数字在内部被转换为十进制表示形式,四舍五入为 14 或 15 位十进制数字 - 497 将四舍五入为 500。
请注意,在拉丁语中,数字以 10 为底(digitus = finger)。
The FLOAT data type is a subtype of NUMBER
所以这是一个 number
幕后和
To convert from binary to decimal precision, multiply n by 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 is available for you to use, but Oracle recommends that
you use the BINARY_FLOAT and BINARY_DOUBLE data types instead, as they
are more robust
如前所述,这是因为此类型下有一个 number
实现。正如您可以使用以下查询检查的那样,二进制精度被完全忽略:
- 二进制精度四舍五入向上到小数精度N.
- 该数字在数学上四舍五入到 前 N 个重要位置(例如,对于 N = 2:101 变为 100,106 变为 110)。
- 数字以
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<>fiddle 这里
如您所见,对于精度 1-3,您只能存储一位数,对于 4-6 - 两位数,等等。您还可以观察到,该比例每两位小数变化一次([ 中的前两个字节) =13=],列 scale
)。
所以存储是:两个字节用于比例,最多 20 个字节用于精度,其中每个字节包含从 0 到 99 的数字。但我无法弄清楚他们在哪里丢失了精度为 38 (19) 的两位数字*2, 应该是 20).
我有一个 table 和 FLOAT
列,如 Oracle docs 中所定义。
我插入一个有 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;
return:
10000000000004, 10000000000001, 10000000000001
这是为什么?我错过了一些基本的浮点数学吗?
链接的文档页面说
A FLOAT value is represented internally as NUMBER
莫非二进制位数四舍五入到十进制位数?
另外,换算成十进制,十六进制数为:
10000000000001 -> 4503599627370497
10000000000004 -> 4503599627370500
所以在我看来,数字在内部被转换为十进制表示形式,四舍五入为 14 或 15 位十进制数字 - 497 将四舍五入为 500。
请注意,在拉丁语中,数字以 10 为底(digitus = finger)。
The FLOAT data type is a subtype of NUMBER
所以这是一个 number
幕后和
To convert from binary to decimal precision, multiply n by 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 is available for you to use, but Oracle recommends that you use the BINARY_FLOAT and BINARY_DOUBLE data types instead, as they are more robust
如前所述,这是因为此类型下有一个 number
实现。正如您可以使用以下查询检查的那样,二进制精度被完全忽略:
- 二进制精度四舍五入向上到小数精度N.
- 该数字在数学上四舍五入到 前 N 个重要位置(例如,对于 N = 2:101 变为 100,106 变为 110)。
- 数字以
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<>fiddle 这里
如您所见,对于精度 1-3,您只能存储一位数,对于 4-6 - 两位数,等等。您还可以观察到,该比例每两位小数变化一次([ 中的前两个字节) =13=],列 scale
)。
所以存储是:两个字节用于比例,最多 20 个字节用于精度,其中每个字节包含从 0 到 99 的数字。但我无法弄清楚他们在哪里丢失了精度为 38 (19) 的两位数字*2, 应该是 20).