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 / 79334260
Accepted
Shantanu Gontia
Shantanu Gontia
Asked: 2025-01-07 04:29:30 +0800 CST2025-01-07 04:29:30 +0800 CST 2025-01-07 04:29:30 +0800 CST

Conversão de Numpy Float para HalfFloat RNE quando o resultado é subnormal

  • 772

Estou tentando entender como o NumPy implementa o arredondamento para o mais próximo, mesmo ao converter para um formato de precisão mais baixa, neste caso, Float32 para Float16, especificamente o caso em que o número é normal em Float32, mas é arredondado para um subnormal em Float16.

Link para o código: https://github.com/numpy/numpy/blob/13a5c4e569269aa4da6784e2ba83107b53f73bc9/numpy/core/src/npymath/halffloat.c#L244-L365

Meu entendimento é o seguinte,

Em float32, o número tem os bits

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
e e0 e1 e2 e3 e4 e5 e6 e7 m0 m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12 m13 m14 m15 m16 m17 m18 m19 m20 m21 m22
        /*
         * If the last bit in the half significand is 0 (already even), and
         * the remaining bit pattern is 1000...0, then we do not add one
         * to the bit after the half significand. However, the (113 - f_exp)
         * shift can lose up to 11 bits, so the || checks them in the original.
         * In all other cases, we can just add one.
         */
        if (((f_sig&0x00003fffu) != 0x00001000u) || (f&0x000007ffu)) {m
            f_sig += 0x00001000u;
        }

O código acima é usado ao quebrar empates para o par mais próximo. Não entendo por que na segunda parte do OR lógico, fazemos AND bit a bit contra 0x0000'07ffu(bits m12-m22) e não 0x0000'ffffu(m11-m22) .

Depois de alinharmos os bits da mantissa para que fiquem no formato subnormal para float16 (que é o que a mudança de bits antes deste pedaço de código faz), na representação numérica float32 acima teríamos m10- m22decidindo qual direção arredondar.

Meu entendimento é que a segunda parte do OR verifica se o número é maior do que o ponto médio, e se for, então adiciona um ao bit meio-significante. Mas com o número original, ele não está apenas verificando um subconjunto dos números que estão acima do ponto médio? No número float16, m9 seria a última precisão que vai permanecer. Então, arredondaremos para cima se,

  1. m9 é 1, m10 é 1 e m11-m22 são todos 0 (A primeira parte do OR)

  2. m10 é 1, pelo menos um de m11-m22 é 1 (para colocar o número acima do ponto médio)

  3. pode ser simplificado adicionando 1 a m10, se qualquer um de m11-m22 for 1. se m10 já for 1, a adição sangrará para m9, caso contrário, permanecerá inalterado. Mas, no caso do código NumPy, os bits verificados são m12-m22.

Não tenho certeza do que estou perdendo aqui. Esse é um cenário de caso especial?

Eu esperava que os bits m11-m22 fossem os que decidiriam se adicionariam 1 e nem m12-m22.

numpy
  • 1 1 respostas
  • 41 Views

1 respostas

  • Voted
  1. Best Answer
    Eric Postpischil
    2025-01-07T06:11:29+08:002025-01-07T06:11:29+08:00

    f_sigcontém um significando-em-preparação para o resultado binary16. ( binary16 é o nome IEEE-754 para o que algumas pessoas chamam de formato de ponto flutuante de “meia precisão”.) Neste ponto, o código precisa dos bits de significando nos bits 22:13, porque mais tarde ele vai deslocá-los em mais 13 bits, colocando-os em 9:0. Em preparação para isso, ele deslocou os bits de acordo com o expoente. Isso deslocou alguns bits de f_sig.

    Agora ele quer testar se o bit baixo do novo significando (agora no bit 13) é 0, o mais alto dos bits abaixo do significando (no bit 12) é 1, e todos os bits restantes são 0. Alguns desses bits restantes estão nos bits 11:0 de f_sig. Mas alguns deles podem ter desaparecido. O deslocamento de acordo com o expoente deslocou alguns deles para fora. Então, para testar se esses bits são 0, olhamos para eles no significando original em f.

    Como o deslocamento do expoente se deslocou para fora no máximo 11 bits, só precisamos olhar para os 11 bits baixos de f. Os outros bits do significando original ainda estão presentes em f_sig.

    Então, em (f_sig&0x00003fffu) != 0x00001000u) || (f&0x000007ffu), o operando esquerdo de ||testa os bits de significando originais que estão f_sige o operando direito testa os bits de significando originais que estão em f. Pode haver alguma sobreposição; o último pode testar alguns bits que também estão em f_sig, mas isso não importa.

    Pelo que entendi, a segunda parte do OR verifica se o número é maior que o ponto médio e, se for, adiciona um ao bit do meio-significante.

    Não, não está verificando isso. O teste é verdadeiro se e somente se a porção final não for exatamente ½ do bit menos significativo (LSB) do novo significando ou o bit menos significativo for 1.

    O raciocínio é este:

    • A declaração controlada, f_sig += 0x00001000u;, adiciona ½ do LSB, e o significando é posteriormente truncado no LSB ( f_sig >> 13). Isso fornece o arredondamento desejado na maioria dos casos: Adicionar ½ às porções finais menores que ½ não carrega, e adicionar ½ às porções finais maiores que ½ carrega.
    • Além disso, nos casos em que a parte final é exatamente ½ e adicionamos ½, a adição é realizada, e esse é o comportamento desejado quando o bit baixo do LSB é 1.
    • Então, o único caso em que não queremos fazer essa adição é quando a parte final é exatamente ½ e a parte baixa do LSB é 0.
    • 2

relate perguntas

  • Como classificar o tensor em "lote" por valor de chave específico?

  • Aviso de descontinuação do notebook Jupyter ao encontrar a raiz do determinante de uma matriz

  • Como você concatena matrizes internas do tensor ao longo do eixo?

  • Digite regra de promoção para i4 e S8 no documento numpy

  • Transmitindo uma matriz numpy em uma matriz de tamanho maior usando uma matriz de índice

Sidebar

Stats

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

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

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

    • 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

    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
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +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

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