为什么对实数和数值输入进行运算会产生双精度?

问题描述 投票:0回答:1

在 PostgreSQL 中对

real
操作数和
numeric
操作数执行数学运算会产生
double precision
值,但我发现这令人惊讶。 为什么结果不是
real
,而是?

我在 PostgreSQL 13 和 16 中证实了这一行为。

SELECT 
   pg_typeof(0::numeric + 0::numeric), -- numeric
   pg_typeof(0::real    + 0::real),    -- real
   pg_typeof(0::numeric + 0::real),    -- double precision??
   pg_typeof(0::real    + 0::numeric); -- double precision??

我已阅读 PostgreSQL 文档的大部分内容,最相关的是:10.2。类型转换:运算符

我已经查询了

pg_catalog.pg_cast
(或
psql
\dC
)以了解相关的隐式转换是什么:

> SET search_path = 'pg_catalog';
> SELECT format_type(castsource, NULL) AS source,
         format_type(casttarget, NULL) AS target
  FROM pg_cast
  WHERE castcontext = 'i' -- i.e. casts allowed implicitly
    AND ARRAY[format_type(castsource, NULL), format_type(casttarget, NULL)] 
        <@ '{numeric,real,double precision}'::text[];

结果是:

 source  |      target
---------+------------------
 real    | double precision
 numeric | real
 numeric | double precision
 numeric | numeric
(4 rows)

所以据我了解,对于

0::numeric + 0::real
,PostgreSQL应该(摘录均来自10.2.类型转换:运算符):

  1. pg_operator
    系统目录中选择要考虑的操作员。如果使用非模式限定的运算符名称(通常情况),则考虑的运算符是具有匹配名称和参数计数且在当前搜索路径中可见的运算符。

我希望它以所有

+
运算符开始 - 每个运算符都接受一对相同的数字类型(
real + real
integer + integer
等)

  1. 检查是否存在完全接受输入参数类型的运算符。

在步骤 2 中,没有运算符与

real
numeric
完全匹配,所以我希望这里不会发生任何事情。

3a。丢弃输入类型不匹配且无法转换(使用隐式转换)来匹配的候选运算符。

我预计这会丢弃大多数候选人。 它应该保留恰好两个:一个用于

real
,另一个用于显然最终使用的
double precision
。 (请注意,
real
不能隐式转换为
numeric
,因此不应保留
numeric
。)

3b。如果任何输入参数属于域类型,则在所有后续步骤中将其视为域的基本类型。

我希望这不会执行任何操作,因为这里没有域类型。

3c。遍历所有候选者并保留那些与输入类型最精确匹配的候选者。如果没有完全匹配的候选者,则保留所有候选者。如果只剩下一名候选者,则使用它;否则继续下一步。

在这里,我希望它应该丢弃

double precision
的运算符,因为
real
的运算符有 1 个精确匹配,而
double precision
的运算符有 0 个精确匹配。 然后,由于这里只剩下一个候选者,它应该使用
real
运算符,我预计结果是
real
。 但相反,结果是一个
double precision
值。

所以,我的问题是,为什么 PostgreSQL 在这种情况下选择

double precision
运算符,它需要隐式转换两个操作数而不是仅其中之一? 是文档错误,还是我的理解错误?

postgresql
1个回答
0
投票

拼图中缺失的两块是:

  1. 有关混合类型数学运算中结果类型的文档

    涉及多个参数数据类型的调用,例如

    integer
    +
    numeric
    ,可通过使用这些列表后面出现的类型来解析。

    相关列表为“

    smallint
    integer
    bigint
    numeric
    real
    double precision
    ”。

  2. 您可以在

    pg_type.ispreferred
    中检查类型偏好。在那里,
    float8
    (
    double precision
    ) 优于
    float4
    (
    real
    ) 以及
    numeric

    select typname
         , typispreferred
    from pg_type 
    where typcategory='N'
    order by typispreferred desc
            ,typname
    
    类型名称 典型首选
    浮动8 T
    T
    基数数字 f
    浮动4 f
    int2 f
    int4 f
    int8 f
    f
    数字 f

前者只是为了使这个答案更通用并适用于其他组合。后者正是为什么您看到

real+numeric
结果是
real+numeric::double precision
产生
double precision
,而不是
real+numeric::real
会给您带来
real

© www.soinside.com 2019 - 2024. All rights reserved.