PostgreSQL中双精度数值的舍入机制及精确存储查询问题
我了解double precision的工作原理(包括指数、尾数、精度随数值变化、部分数值无法精确存储等特性),目前正在撰写相关主题的博客。但在使用PostgreSQL时,发现它会对这类数值做舍入处理。
配置参数extra_float_digits=1(或更大值)本该允许PostgreSQL显示所有位数,但实际效果并非如此。
比如,1.1无法用double precision精确存储,它会被近似为1.100000000000000088817841970012523233890533447265625——这是最接近的可存储值,相邻步长约为2.2204460492 × 10^−16。为什么没法直接可视化这个存储的精确值?
我猜测代码会把数值舍入到10^-15位,因为这是该数量级下的最大精度,但没法确认。
我有三个问题:
- PostgreSQL是否会对数值进行舍入?
- 是否可以查看存储的精确值?
- 是否实际已能查看,但我在使用PSQL时操作有误?
测试操作及结果
我设置了:
SET extra_float_digits=3;
执行查询:
SELECT 1.1::float8;
返回结果:
float8 -------- 1.1
结果为1.1,并非实际存储的精确值。
同样执行:
SELECT 1.1::float8::varchar;
返回结果:
varchar --------- 1.1
若直接查询精确值:
SELECT 1.100000000000000088817841970012523233890533447265625::float8;
返回结果:
float8 -------- 1.1
问题解答
PostgreSQL是否会对数值进行舍入?
是的。PostgreSQL在显示float8(即double precision)类型的值时,会自动舍入为最短的十进制字符串——这个字符串转换回float8时能还原出原始的二进制存储值。这种处理是为了避免冗余位数,同时保证数值的可逆性,并非丢失了存储的精确值。是否可以查看存储的精确值?
可以。你需要用to_char函数指定足够多的小数位数,或者通过查看二进制原始值来推导精确十进制表示:
- 使用
to_char显示精确值:
SELECT to_char(1.1::float8, 'FM9999999999999999999999999999');
该查询会返回1.100000000000000088817841970012523233890533447265625。
- 查看二进制原始值:
SELECT encode(cast(1.1::float8 as bytea), 'hex');
得到的十六进制值可以转换为对应的十进制精确值。
- 是否实际已能查看,但我在使用PSQL时操作有误?
是的。你之前的操作没有采用能显示全部精确位数的方法。extra_float_digits参数控制的是显示的额外位数,但它的设计目标是保证数值转换的可逆性,而非强制显示所有二进制位对应的十进制位数。默认的格式化逻辑还是优先输出最短的可逆字符串,所以即使设置了extra_float_digits=3,也不会自动显示全部精确位数。
内容的提问来源于stack exchange,提问作者Arthur Bazin

