Flutuar 754 em Hamming

29

Você receberá como entrada um número inteiro kno intervalo de -4503599627370496(−2 52 ) a 4503599627370496(2 52 ). Como é sabido , números inteiros nesse intervalo podem ser representados exatamente como valores de ponto flutuante de precisão dupla.

Você deve saída o peso de Hamming (número de ones) da codificação de kno formato binary64 . Isso usa 1 bit para o sinal, 11 bits para o expoente (codificado com um deslocamento) e 52 para a mantissa; veja o link acima para detalhes.

Como exemplo , o número 22é representado como

0 10000000011 0110000000000000000000000000000000000000000000000000

Como existem 5, a saída é 5.

Observe que o endianness não afeta o resultado; portanto, você pode usar com segurança a representação interna real de valores de dupla precisão da sua máquina para calcular a saída.

Regras adicionais

Casos de teste

22                ->   5
714               ->   6
0                 ->   0
1                 ->  10
4503599627370496  ->   5
4503599627370495  ->  55
1024              ->   3
-1024             ->   4
-4096             ->   5
1000000000        ->  16
-12345678         ->  16
Luis Mendo
fonte
11
Você pretende que as funções possam aceitar suas entradas já no binary64formato de ponto flutuante , se quiserem? Algumas pessoas (inclusive eu, inicialmente) estavam interpretando a pergunta como exigindo que as funções aceitassem entradas como um tipo inteiro como Cs long. Em C, você pode argumentar que o idioma será convertido para você, assim como quando você liga sqrt((int)foo). Mas existem algumas respostas asm de código de máquina x86 (como codegolf.stackexchange.com/a/136360/30206 e mine) que ambas estavam assumindo que precisávamos aceitar entradas inteiras de 64 bits. Aceitar um binary64valor economizaria 5 bytes.
Peter Cordes
Nesse caso, todo esse material sobre alcance limitado é apenas para o caso de alguém querer hackear a conversão para um padrão de bits binário64 em vez de punição de tipo? Ou para idiomas sem digitar? Hmm, um desafio interessante pode ser adicionar o expoente e a mantissa de a binary64como números inteiros base2. Se você precisar manipulá-los separadamente de qualquer maneira, pode valer a pena fazer algo diferente de digitar pun e repetir todos os bits.
Peter Cordes
2
@PeterCordes Sim, você pode inserir na forma de um número de ponto flutuante. O alcance limitado é garantir que a representação de ponto flutuante seja precisa.
Luis Mendo
Ok obrigado. Eu acho que você queria deixar a opção de escrever uma função que aceita a long, então você não pode simplesmente dizer qualquer binary64 double, porque nem todas as duplas são inteiras. Mas todos os doubles com valor inteiro podem ser convertidos longe retornados, até os limites de long. (Como você aponta, o inverso não é verdadeiro. Você obtém o representante mais próximo double, assumindo o modo de arredondamento padrão). Enfim, essa era uma maneira totalmente válida de fazer a pergunta; Eu só não lê-lo com cuidado> <.
Peter Cordes
"Observe que o endianness não afeta o resultado; portanto, você pode usar com segurança a representação interna real de valores de dupla precisão da sua máquina para calcular a saída". a menos que a sua máquina não usar o formato de ponto flutuante IEEE ...
Jerry Jeremias

Respostas:

8

MATL , 5 bytes

3Z%Bz

Experimente online!

Transliteração exata da minha resposta do MATLAB. Observe que entrada e saída estão implícitas. -2 bytes graças a Luis Mendo.

3Z%   % Typecast: changes input (implicitly taken and converted to double) to uint64 without changing underlying bits
B     % Convert integer to array of 1s and 0s
z     % Count nonzero entries
Sanchises
fonte
33

linguagem de máquina x86_64 (Linux), 16 bytes

0:       f2 48 0f 2a c7          cvtsi2sd %rdi,  %xmm0
5:       66 48 0f 7e c0          movq     %xmm0, %rax
a:       f3 48 0f b8 c0          popcnt   %rax,  %rax
f:       c3                      retq

Aceita um único parâmetro inteiro de 64 bits RDI, converte-o para um valor de ponto flutuante XMM0, armazena esses bits novamente RAXe calcula o peso de hamming de RAX, deixando o resultado RAXpara que ele possa ser retornado ao chamador.

Requer um processador que suporte a POPCNTinstrução, que seria Intel Nehalem, AMD Barcelona e microarquiteturas posteriores.

Para experimentá-lo online! , compile e execute o seguinte programa C:

#include<stdio.h>
const char g[]="\xF2\x48\x0F\x2A\xC7\x66\x48\x0F\x7E\xC0\xF3\x48\x0F\xB8\xC0\xC3";
#define f(x) ((int(*)(long))g)(x)

int main(int a){
  printf("%d\n",f(22));
  printf("%d\n",f(714));
  printf("%d\n",f(0));
  printf("%d\n",f(1));
  printf("%d\n",f(4503599627370496L));
  printf("%d\n",f(4503599627370495L));
  printf("%d\n",f(1024));
  printf("%d\n",f(-1024));
  printf("%d\n",f(-4096));
  printf("%d\n",f(1000000000));
  printf("%d\n",f(-12345678));
}
teto
fonte
2
+1, a ferramenta certa para o trabalho! Essa pode ser a única vez que o x86 pode competir legitimamente com os idiomas do golfe ou vencer o Jelly. :)
DJMcMayhem
2
Ew, sintaxe da AT&T? Você pode usar objdump -drwC -Mintelpara desmontar na sintaxe da Intel. Se você tivesse um ponteiro em um registro que pudesse usar para armazenar / recarregar, poderia salvar bytes com movaps [rsi], xmm0/ popcnt rax, [rsi]. (movaps é de apenas 3 bytes, 2 menores que o movq.) Mas isso não ajuda aqui, porque [rsp-24]leva 2 bytes extras (o SIB usa o RSP como base, mais disp8). E esses bytes extras são necessários na loja e na recarga. Oh bem, eu pensei ter visto uma economia, mas não: /
Peter Cordes
I salvo 4 bytes com uma convenção telefónico feito sob encomenda . Ou ainda salve 2 bytes com a mesma convenção de chamada, usando as instruções x87.
Peter Cordes
11
@ DJMcMayhem: Talvez não seja a única vez. Ainda não há respostas no idioma do golfe para o desafio Extreme Fibonacci (imprima os primeiros 1000 dígitos de Fib (1 bilhão) e a resposta do meu código de máquina x86 (105 bytes rápidos, ou 101 bytes em execução em 5 minutos em vez de 1 minuto)) não é muito maior do que algumas das outras respostas, e eles estão todos em línguas com números inteiros de precisão estendida construídas dentro.
Peter Cordes
2
Ou um desafio mais simples (e sem um requisito de desempenho), a chroma-key misturando uma matriz de números inteiros . Minha resposta de código de máquina tem metade do comprimento da resposta de pyth.
27516 Peter Cordes
11

C (gcc) , 82 68 bytes

9 bytes graças a Neil.

hacking de nível de bit de ponto flutuante mal

s;f(long n){double d=n;n=*(long*)&d;for(s=0;n;n*=2)s+=n<0;return s;}

Experimente online!

Freira Furada
fonte
Eu sabia que você seria o primeiro, mas não esperava a língua :-D
Luis Mendo
@LuisMendo Eu apenas pensei que seria conveniente nessa língua ... Eu não sei de outras línguas que podem fazer isso
Leaky Nun
2
Salve 9 bytes mudando para o outro lado: ... ;long l=... ;l*=2;)s+=l<0;...
Neil
11
É claro que isso requer uma implementação C com 64 bits long. Funciona no Linux x86-64, mas falharia no Windows. Eu sugiro dizer "gcc com 64 bits long", já que o gcc é executado em muitas plataformas, muitas delas com ABIs diferentes.
Peter Cordes
11
O comentário de @ Peter é o motivo pelo qual eu adicionei "LP64" em uma edição. Também reorganizei o outro texto no que eu pensava ser uma ordem mais lógica. Eu acho que você não gostou dessa alteração e a reverteu, mas LP64 é um termo padrão que descreve a ABI em que longos e ponteiros são valores de 64 bits (em comparação com ILP64, onde ints também são de 64 bits ou LLP64, conforme usado no Windows, onde apenas longos e ponteiros são de 64 bits e longos ainda de 32 bits). Talvez eu devesse ter adicionado mais explicações ou um link embutido para o artigo relevante da Wikipedia.
Cody Gray
8

Python 3 , 72 71 bytes

1 byte graças a Lynn.

lambda n:n and(bin(1020+len(bin(abs(n))))+bin(abs(n))).count('1')-(n>0)

Experimente online!

Explicação

O formato binary64 consiste em três componentes:

  • o primeiro bit é o sinal, ou seja, 1se o número for negativo
  • os próximos 11 bits armazenam o expoente com 1023 adicionados
  • os próximos 52 bits armazenam o significando, ou a mantissa.
Freira Furada
fonte
n and(…)-(n>0)é um byte mais curto, não?
Lynn
Ou int-> float, ou quaisquer flutuadores, para esse assunto.
User2357112 suporta Monica
8

C (gcc) , 47 bytes

f(double n){n=__builtin_popcountl(*(long*)&n);}

Isso não é portátil; foi testado com o gcc 7.1.1 no x86_64 executando o Linux, sem sinalizadores do compilador.

Experimente online!

Dennis
fonte
11
A entrada deve ser um número inteiro. Ou é permitido deixar o chamador lidar com isso por conversão implícita de longpara doubleno site da chamada?
Peter Cordes
11
Além disso, contando com um golpe de sorte do comportamento compilador para acontecer a deixar nem raxcom código-un otimizado é bastante bregas. Ele quebra se você ativar -O3, portanto, não é apenas o gcc em geral, é o gcc no x86-64 com 64 bits longcom a otimização desativada. Se você colocar todos esses requisitos em sua resposta, eu votaria. Eu diria que existem plataformas suportadas pelo gcc com 64 bits, longmas que deixam o popcountlresultado em um registro diferente do registro de valor retornado.
Peter Cordes
11
Tomei inteiro no sentido matemático. Adicionei as especificações dos meus ambientes de teste, pois não tenho certeza de que os gcc, x86-64 e 64 bits sejam suficientes. Dito isto, pelo menos no x86, as funções sem retorno funcionam com gcc (e tcc) com mais frequência.
Dennis
Sim, eu estava apenas relendo a pergunta e concordo que aceitar o argumento como doubledeveria estar bem. Não diz nada sobre exigir que a função aceite-a no formato base2. E sim, versões diferentes do gcc podem emitir códigos diferentes, então isso é importante também. (Curiosidade: sem -mpopcnt, o gcc não usará o popcntinsn e emitirá uma sequência de instruções para emulá-lo. Algumas arquiteturas não têm nenhuma instrução popcnt, portanto, __builtin_popcountlsempre é necessário usar alguma sequência de insns)
Peter Cordes
Sim, muitas __builtin_*funções (a maioria?) Possuem versões herdadas para evitar a geração de instruções ilegais. -march=nativeusa popcntqsomente se estiver disponível.
217 Dennis
6

C (gcc), 63 bytes

f(double d){long s=0,n=*(long*)&d;for(;n;n*=2)s+=n<0;return s;}

Esta solução é baseada na resposta de @ LeakyNun, mas como ele não quer melhorar sua própria resposta, estou postando aqui uma versão mais elaborada.

Experimente online


fonte
2
Eu duvido muito que alguém não queira melhorar sua resposta.
Mr. Xcoder
11
@ Mr.Xcoder. Ok, vou manter isso aqui até que ele edite sua própria resposta. Se ele não quiser editar, isso ficará aqui. Postei essa melhoria como um comentário em sua resposta e ele a rejeitou.
11
Eu acho que a entrada precisa ser um tipo inteiro e não real.
ceilingcat
3
@ThePirateBay Não vi o seu comentário na minha resposta e ainda não o vejo agora.
Leaky Nun
9
A decisão de sugerir melhorias ou postar sua própria resposta é sua, mas 6 minutos não duram cerca de uma hora .
Dennis
5

C #, 81 70 68 bytes

d=>{unsafe{long l=*(long*)&d,s=0;for(;l!=0;l*=2)s-=l>>63;return s;}}

Economize 11 bytes graças ao @Leaky Nun.
Economizou 2 bytes graças a @Neil.

Experimente online! Utiliza em System.BitConverter.DoubleToInt64Bitsvez do unsafecódigo, pois não consegui que o TIO trabalhasse com ele.

Versão completa / formatada:

namespace System
{
    class P
    {
        static void Main()
        {
            Func<double, long> f = d =>
            {
                unsafe
                {
                    long l = *(long*)&d, s = 0;

                    for (; l != 0; l *= 2)
                        s -= l >> 63;
                    return s;
                }
            };

            Console.WriteLine(f(22));
            Console.WriteLine(f(714));
            Console.WriteLine(f(0));
            Console.WriteLine(f(1));
            Console.WriteLine(f(4503599627370496));
            Console.WriteLine(f(4503599627370495));
            Console.WriteLine(f(1024));
            Console.WriteLine(f(-1024));
            Console.WriteLine(f(-4096));
            Console.WriteLine(f(1000000000));
            Console.WriteLine(f(-12345678));

            Console.ReadLine();
        }
    }
}
TheLethalCoder
fonte
for(;l!=0;l*=2)e você não vai precisar do ternário
Leaky Nun
@LeakyNun Obrigado, eu estava coçando a cabeça por muito tempo.
TheLethalCoder
Você pode usar s-=l>>31?
Neil
@ Neil Não parece funcionar. Eu suponho que você pretende substituir s+=l<0?1:0?
TheLethalCoder
Minha culpa; lé um longo, então ele precisa s-=l>>63?
Neil
4

Python 2 , 69 bytes

-12 bytes, graças a @ ASCII-only

lambda n:bin(*unpack('Q',pack('d',n))).count('1')
from struct import*

Experimente online!

Gambá morto
fonte
11
71 bytes
somente ASCII
11
Golfe sua abordagem, 76 bytes , eu recomendo de ASCII-only abordagem embora
Mr. Xcoder
@ Mr.Xcoder !não é necessário, pois a ordem dos bytes não importa aqui
ASCII-only
11
69 bytes
somente ASCII
@ Somente ASCII Embale a embalagem. Obrigado: D
Dead Possum
4

JavaScript (ES6), 81 80 77 bytes

f=
n=>new Uint8Array(Float64Array.of(n).buffer).map(g=i=>i&&g(i^i&-i,x++),x=0)|x
<input oninput=o.textContent=f(this.value)><pre id=o>0

Editar: salvou 1 byte graças a @Arnauld. Economizou 3 bytes graças a @DocMax.

Neil
fonte
Você poderia fazer g(i^i&-i,x++)por -1 byte?
Arnauld
@ Arnauld Eu me perguntei se havia um pouco de taco de golfe, obrigado por encontrá-lo!
315 Neil
11
-3 mais se você substituir new Float64Array([n])porFloat64Array.of(n)
DocMax 29/07
4

código de máquina x86-64, 12 bytes para int64_tentrada

6 bytes para doubleentrada

Requer a popcntextensão ISA ( CPUID.01H:ECX.POPCNT [Bit 23] = 1).

(Ou 13 bytes, se a modificação do argumento no local exigir a gravação de todos os 64 bits, em vez de deixar o lixo nos 32 superiores. Acho razoável argumentar que o chamador provavelmente só desejaria carregar os 32b baixos de qualquer maneira e x86 zero - estende de 32 a 64 implicitamente em todas as operações de 32 bits. Ainda assim, impede o chamador de fazer add rbx, [rdi]algo assim.)

As instruções x87 são mais curtas que o SSE2 cvtsi2sd/ mais óbvio movq(usado na resposta do @ ceilingcat ) e um [reg]modo de endereçamento é do mesmo tamanho que um reg: apenas um byte mod / rm.

O truque era encontrar uma maneira de passar o valor na memória, sem precisar de muitos bytes para os modos de endereçamento. (por exemplo, passar a pilha não é tão bom.) Felizmente, as regras permitem args de leitura / gravação ou args de saída separados , para que eu possa fazer com que o chamador me passe um ponteiro para a memória que tenho permissão para escrever.

É possível chamar a partir de C com a assinatura: void popc_double(int64_t *in_out); apenas os 32b baixos do resultado são válidos, o que talvez seja estranho para C, mas natural para asm. (A correção disso requer um prefixo REX no armazenamento final ( mov [rdi], rax), portanto, mais um byte.) No Windows, altere rdipara rdx, pois o Windows não usa a ABI do System V x86-64.

Listagem NASM. O link TIO possui o código fonte sem a desmontagem.

  1  addr    machine      global popcnt_double_outarg
  2          code         popcnt_double_outarg:
  3                           ;; normal x86-64 ABI, or x32: void pcd(int64_t *in_out)
  4 00000000 DF2F             fild qword  [rdi]    ; int64_t -> st0
  5 00000002 DD1F             fstp qword  [rdi]    ; store binary64, using retval as scratch space.
  6 00000004 F3480FB807       popcnt rax, [rdi]
  7 00000009 8907             mov    [rdi], eax    ; update only the low 32b of the in/out arg
  8 0000000B C3               ret
    # ends at 0x0C = 12 bytes

Experimente online! Inclui um_startprograma de teste que transmite um valor e sai com exit status = popcnt return value. (Abra a guia "debug" para vê-lo.)

Passar ponteiros de entrada / saída separados também funcionaria (rdi e rsi na ABI do x86-64 SystemV), mas não podemos destruir razoavelmente a entrada de 64 bits ou justificar facilmente a necessidade de um buffer de saída de 64 bits enquanto apenas grava o baixo 32b.

Se quisermos argumentar que podemos pegar um ponteiro para o inteiro de entrada e destruí-lo, enquanto retornamos a saída rax, simplesmente omita o mov [rdi], eaxfrom popcnt_double_outarg, diminuindo-o para 10 bytes.


Alternativa sem truques bobos de convenções de chamada, 14 bytes

use a pilha como espaço de trabalho, pushpara chegar lá. Use push/ poppara copiar registros em 2 bytes em vez de 3 para mov rdi, rsp. ( [rsp]sempre precisa de um byte SIB, vale a pena gastar 2 bytes para copiar rspantes de três instruções que o utilizam.)

Ligue de C com esta assinatura: int popcnt_double_push(int64_t);

 11                               global popcnt_double_push
 12                               popcnt_double_push:
 13 00000040 57                       push   rdi         ; put the input arg on the stack (still in binary integer format)
 14 00000041 54                       push   rsp         ; pushes the old value (rsp updates after the store).
 15 00000042 5A                       pop    rdx         ; mov      rdx, rsp
 16 00000043 DF2A                     fild   qword [rdx]
 17 00000045 DD1A                     fstp   qword [rdx]
 18 00000047 F3480FB802               popcnt rax,  [rdx]
 19 0000004C 5F                       pop    rdi         ; rebalance the stack
 20 0000004D C3                       ret
    next byte is 0x4E, so size = 14 bytes.

Aceitando entrada em doubleformato

A questão apenas diz que é um número inteiro em um determinado intervalo, não que ele precise estar em uma representação de número inteiro binário base2. Aceitar doubleentrada significa que não há mais sentido usar x87. (A menos que você use uma convenção de chamada personalizada onde doubles são passados ​​nos registros x87. Em seguida, armazene na zona vermelha abaixo da pilha e popcnt a partir daí.)

11 bytes:

 57 00000110 66480F7EC0               movq    rax, xmm0
 58 00000115 F3480FB8C0               popcnt  rax, rax
 59 0000011A C3                       ret

Mas podemos usar o mesmo truque de passagem por referência de antes para criar uma versão de 6 bytes: int pcd(const double&d);

 58 00000110 F3480FB807               popcnt  rax, [rdi]
 59 00000115 C3                       ret

6 bytes .

Peter Cordes
fonte
4

Perl 5 , 33 32 + 1 (-p) = 34 33 bytes

Guardado 1 byte graças a hobbs

$_=(unpack"B*",pack d,$_)=~y/1//

Experimente online!

Xcali
fonte
Você pode economizar 1 byte criando duma palavra de barra (pack d,$_ em vez de pack"d",$_)
hobbs
3

MATLAB, 36 bytes

@(n)nnz(de2bi(typecast(n,'uint64')))

Usar o fato de que de2binão é apenas mais curto que dec2bin, mas também fornece um resultado em uns e zeros, em vez de ASCII 48, 49.

Sanchises
fonte
3

Java (64, 61, 41 bytes)

Completamente simples usando a biblioteca padrão (Java SE 5+):

int f (long n) {return Long. bitCount (Double. doubleToLongBits (n));}

Contribuição de Kevin Cruijssen (Java SE 5+):

int f(Long n){return n.bitCount(Double.doubleToLongBits(n));}

Contribuição de Kevin Cruijssen (Java SE 8+, função lambda):

n->n.bitCount(Double.doubleToLongBits(n))
Nayuki
fonte
Bem feito! :-)
Leaky Nun
11
Boa resposta, +1 de mim. Você pode jogar três bytes de golfe usando o parâmetro como Long ne use em n.bitCount(...)vez de Long.bitCount(...). Além disso, se você usar Java 8+, você pode golfe para n->n.bitCount(Double.doubleToLongBits(n))( 41 bytes )
Kevin Cruijssen
2

Apenas para tentar uma abordagem diferente e mais segura do que TheLethalCoder , eu vim com isso (é uma pena que o C # tenha nomes de métodos tão longos):

C # (.NET Core) , 76 + 13 bytes

d=>Convert.ToString(BitConverter.DoubleToInt64Bits(d),2).Split('1').Length-1

Experimente online!

A contagem de bytes inclui 13 bytes para using System;. Primeiro, preciso converter o doublepara um longque tenha a mesma representação binária, depois posso convertê-lo em um binário stringe depois contar os 1s apenas dividindo a string e contando as substrings menos 1.

Charlie
fonte
Boa alternativa, mas você precisa incluir o valor usingna sua contagem de bytes.
TheLethalCoder
Use Linq para 95 bytes apenas mais alguns: namespace System.Linq;{d=>Convert.ToString(BitConverter.DoubleToInt64Bits(d),2).Count(c=>c>48)}. Embora eu não o tenha testado, ele deve funcionar.
TheLethalCoder
@TheLethalCoder funciona, mas tentei evitar o Linq para não precisar adicionar um segundo using diretiva.
317 Charlie
11
Quando você adiciona o segundo, é quando namespaceé útil. Mas sim, neste caso, evitar o Linq era um pouco mais barato. Só queria comentar com sua abordagem, caso você tenha alguma idéia de como encurtá-la para economizar bytes.
TheLethalCoder
@TheLethalCoder, Sum(c=>c&1)é mais curto. OuSum()-768
Peter Taylor
1

dc, 79 bytes

[pq]su[-1r]st0dsb?dd0=u0>tsa[1+]ss[la2%1=slb1+sblad2/sa1<r]dsrxlb1022+sa0lrx+1-

A saída é deixada no topo da pilha.
Vou adicionar uma explicação mais tarde.

Experimente online!

Observe que números negativos são precedidos por _, não -.

poi830
fonte
1

C, 67 bytes

int i;g(char*v){int j=v[i/8]&1<<i%8;return!!j+(++i<64?g(v):(i=0));}

código de controle e resultados

#define R     return
#define u32 unsigned
#define F        for
#define P     printf

int main()
{/*           5   6 0 10                5               55    3      4       16*/
 double v[]={22,714,0,1 ,4503599627370496,4503599627370495,1024, -1024, -12345678};
 int i; 

 F(i=0;i<9;++i)
     P("%f = %d\n", v[i], g(&v[i]));
 R 0;
}

>tri4
22.000000 = 5
714.000000 = 6
0.000000 = 0
1.000000 = 10
4503599627370496.000000 = 5
4503599627370495.000000 = 55
1024.000000 = 3
-1024.000000 = 4
-12345678.000000 = 16
RosLuP
fonte