提问人:ihadanny 提问时间:1/11/2021 更新时间:9/20/2021 访问量:989
Oracle 中的数值精度
numeric precision in Oracle
问:
我有一个带有列的表,如 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
为什么?我是否遗漏了一些基本的浮点数学?
答:
链接的文档页面显示
FLOAT 值在内部表示为 NUMBER
难道二进制位数四舍五入到十进制数吗?
此外,转换为十进制,十六进制数字为:
10000000000001 -> 4503599627370497
10000000000004 -> 4503599627370500
所以在我看来,这个数字在内部被转换为十进制表示,四舍五入到 14 或 15 位十进制数字 - 497 将四舍五入到 500。
请注意,在拉丁语中,数字是以 10 为基数(digitus = finger)。
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(50)
0x1.0000000000000p+53
如前所述,这是因为此类型下有一个实现。正如您可以使用以下查询检查的那样,二进制精度被完全忽略:number
- 二进制精度向上舍入到十进制精度 N。
- 该数字在数学上四舍五入到前 N 个有效位置(例如,对于 N = 2:101 变为 100,106 变为 110)。
- 该数字以格式存储,具有步骤 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 - 两位数字,依此类推。您还可以观察到,该刻度每两个十进制数字(列中的前两个字节)更改一次。dump
scale
因此,存储是:两个字节用于缩放,最多 20 个字节用于精度,其中每个字节保存从 0 到 99 的数字。但我无法弄清楚他们在精度 38(19*2,应该是 20)时丢失了两位数。
评论