AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79029325
Accepted
XXB
XXB
Asked: 2024-09-27 06:39:28 +0800 CST2024-09-27 06:39:28 +0800 CST 2024-09-27 06:39:28 +0800 CST

Por que uma operação em entradas reais e numéricas resulta em precisão dupla?

  • 772

Executar uma operação matemática em um realoperando e um numericoperando no PostgreSQL resulta em um double precisionvalor, mas acho isso surpreendente. Por que o resultado não seria um real, em vez disso?

Confirmei esse comportamento com o PostgreSQL 13 e 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??

Li partes substanciais da documentação do PostgreSQL, mais relevantes: 10.2. Conversão de tipo: operadores

E eu consultei pg_catalog.pg_cast(ou psql's \dC) para entender quais são as conversões implícitas relevantes:

> 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[];

O resultado é:

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

Então, pelo que entendi, para 0::numeric + 0::realo PostgreSQL deveria (os trechos são todos de 10.2. Conversão de Tipo: Operadores ):

  1. Selecione os operadores a serem considerados do pg_operatorcatálogo do sistema. Se um nome de operador não qualificado por esquema foi usado (o caso usual), os operadores considerados são aqueles com o nome correspondente e a contagem de argumentos que são visíveis no caminho de pesquisa atual.

Espero que comece com todos +os operadores - cada um aceita um par dos mesmos tipos numéricos ( real + real, integer + integer, etc.)

  1. Verifique se há um operador que aceita exatamente os tipos de argumentos de entrada.

Na etapa 2, nenhum operador é uma correspondência exata para reale numeric, então espero que nada aconteça aqui.

3a. Descarte operadores candidatos cujos tipos de entrada não correspondem e não podem ser convertidos (usando uma conversão implícita) para corresponder.

Espero que isso descarte a maioria dos candidatos. Ele deve manter exatamente dois: o de real, e o de double precisionque ele aparentemente acaba usando. (Note que realnão pode ser implicitamente convertido para numeric, então ele não deve manter o de numeric.)

3b. Se qualquer argumento de entrada for de um tipo de domínio, trate-o como sendo do tipo base do domínio para todas as etapas subsequentes.

Espero que isso não faça nada, pois não há tipos de domínio aqui.

3c. Execute todos os candidatos e mantenha aqueles com as correspondências mais exatas em tipos de entrada. Mantenha todos os candidatos se nenhum tiver correspondências exatas. Se apenas um candidato permanecer, use-o; caso contrário, continue para a próxima etapa.

Aqui, espero que ele descarte o operador for double precision, porque o operador for realtem uma correspondência exata, enquanto o for double precisiontem zero correspondências exatas. E então, como só resta um candidato aqui, ele deve usar o operador for real, e espero que o resultado seja um real. Mas, em vez disso, o resultado é um double precisionvalor.

Então, minha pergunta é por que o PostgreSQL está escolhendo o double precisionoperador neste caso, onde ele precisa converter implicitamente ambos os operandos em vez de apenas um deles? A documentação está errada, ou meu entendimento dela está errado?

postgresql
  • 1 1 respostas
  • 71 Views

1 respostas

  • Voted
  1. Best Answer
    Zegarek
    2024-09-27T11:24:17+08:002024-09-27T11:24:17+08:00

    As duas peças que faltam:

    1. O documento sobre tipos de resultados em operações matemáticas de tipo misto:

      Chamadas envolvendo vários tipos de dados de argumentos, como integer+ numeric, são resolvidas usando o tipo que aparece posteriormente nessas listas.

      A lista em questão é " smallint, integer, bigint, numeric, real, edouble precision ". Essa é a regra geral, mas é uma consequência de quais operadores são incorporados, discutidos abaixo.

    2. Preferência de tipo. Você pode verificar em pg_type.ispreferred. Lá, double precisionaparece como o tipo preferido sobre ambos, realbem como sobre numeric, no grupo Tipos numéricos :

      select typname
           , typispreferred
      from pg_type 
      where typcategory='N'--N for Numeric types
      order by typispreferred desc
              ,typname
      
      nome do tipo tipicamente preferido
      float8
      (precisão dupla)
      Verdadeiro
      oide Verdadeiro
      float4
      (real)
      falso
      int2
      (pequenoint)
      falso
      int4
      (int)
      falso
      int8
      (int grande)
      falso
      dinheiro falso
      numérico falso

      Você parou em 10.2. Conversão de tipo: Operadores logo antes de chegar ao passo que ele usa:

      3d. Execute todos os candidatos e mantenha aqueles que aceitam tipos preferenciais (da categoria de tipo do tipo de dados de entrada)

      Ele vai para 3d, porque em 3c ele descobre que não há real+numericoperador (nenhuma correspondência exata, três meias correspondências):

      select oprleft::regtype
           , oprright::regtype
           , oprresult::regtype
      from pg_operator
      where '{real, double precision,numeric}' 
          && array[oprleft,oprright]::regtype[]
        and oprname='+';
      
      esquerda superior direito de opr resultado
      real real real
      precisão dupla precisão dupla precisão dupla
      real precisão dupla precisão dupla
      precisão dupla real precisão dupla
      numérico numérico numérico
      - real real
      - precisão dupla precisão dupla
      - numérico numérico
      pg_lsn numérico pg_lsn
      numérico pg_lsn pg_lsn

      E dos três candidatos mais próximos, real+real(elenco para a direita) real+double precision(elenco para a direita) e numeric+numeric(elenco para a esquerda), o do meio vence, graças ao seu tipo de destino ser o preferido.

    O primeiro é apenas para tornar esta resposta mais genérica e aplicar a outras combinações. O segundo é precisamente o motivo pelo qual você vê real+numericresultado em real+numeric::double precisionque produz um double precision, em vez de um real+numeric::realque lhe daria um real.

    por que o PostgreSQL está escolhendo o operador de precisão dupla neste caso, onde ele precisa converter implicitamente ambos

    Ele converte apenas um, o numeric. Seu teste usou constantes que são dobradas por constantes, deixando apenas o resultado pré-avaliado e ocultando as operações subjacentes. Você pode ver isso mais claramente se usar explain verbosee uma tabela com valores diferentes:
    demo em db<>fiddle

    EXPLAIN VERBOSE
    SELECT pg_typeof(n+n) nn
         , pg_typeof(r+r) rr
         , pg_typeof(n+r) nr
         , pg_typeof(r+n) rn
    FROM test;
    
    Seq Scan em public.test (custo=0,00..51,23 linhas=970 largura=20)
    Saída: pg_typeof((n + n))
                 , pg_typeof((r + r))
                 , pg_typeof(((n)::precisão dupla + r))
                 , pg_typeof((r + (n)::precisão dupla))
    • 5

relate perguntas

  • Como usar jackc/pgx com pool de conexão, contexto, instruções preparadas etc.

  • Escreva uma consulta psql para localizar usuários em um intervalo de tempo

  • O caractere de escape de barra invertida do Postgresql não funciona para vírgulas em COPY FROM

  • biblioteca postgresql não encontrada no contêiner docker baseado em clojure

  • A inserção do Postgres está acontecendo apesar de violar a restrição de índice parcial

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve