Galleggia da 754 a Hamming


29

Ti verrà dato come input un numero intero knell'intervallo da -4503599627370496(−2 52 ) a 4503599627370496(2 52 ). Come è noto , i numeri interi in questo intervallo possono essere rappresentati esattamente come valori a virgola mobile a precisione doppia.

Dovresti generare il peso di Hamming (numero di quelli) della codifica kin formato binario64 . Questo utilizza 1 bit per il segno, 11 bit per l'esponente (codificato con un offset) e 52 per la mantissa; vedere il link sopra per i dettagli.

Ad esempio , il numero 22è rappresentato come

0 10000000011 0110000000000000000000000000000000000000000000000000

Dal momento che ci sono 5quelli, l'output è 5.

Si noti che l'endianness non influisce sul risultato, quindi è possibile utilizzare in modo sicuro l'effettiva rappresentazione interna della macchina dei valori di doppia precisione per calcolare l'output.

Regole aggiuntive

Casi test

22                ->   5
714               ->   6
0                 ->   0
1                 ->  10
4503599627370496  ->   5
4503599627370495  ->  55
1024              ->   3
-1024             ->   4
-4096             ->   5
1000000000        ->  16
-12345678         ->  16

1
Intendi che le funzioni possono accettare i loro input già nel binary64formato a virgola mobile se lo desiderano? Alcune persone (me compreso, inizialmente) interpretavano la questione richiede che funzioni accettano ingressi come un tipo intero come la C di long. In C, puoi sostenere che la lingua verrà convertita per te, proprio come quando chiami sqrt((int)foo). Ma ci sono alcune risposte asm di codice macchina x86 (come codegolf.stackexchange.com/a/136360/30206 e le mie) che presumevano entrambi che dovevamo accettare input interi a 64 bit. Accettare un binary64valore risparmierebbe 5 byte.
Peter Cordes,

Se è così, allora tutto ciò che riguarda la portata limitata è solo nel caso in cui qualcuno volesse hackerare la conversione in un bit-pattern binario64 invece di punzonare? O per le lingue senza punzonatura? Hmm, una sfida interessante potrebbe essere quella di aggiungere l'esponente e la mantissa di un binary64intero come base2. Se è necessario gestirli separatamente, comunque, potrebbe valere la pena fare qualcosa di diverso da digitare-pun e passare da un loop all'altro.
Peter Cordes,

2
@PeterCordes Sì, è possibile inserire un numero in virgola mobile. L'intervallo limitato è quello di assicurarsi che la rappresentazione in virgola mobile sia accurata
Luis Mendo,

Ok grazie. Immagino che tu volessi lasciare l'opzione di scrivere una funzione che accetta un long, quindi non puoi semplicemente dire binary64 double, perché non tutti i doppi sono numeri interi. Ma tutti i valori interi doublepossono essere convertiti in longe viceversa, fino ai limiti di long. (Come sottolineato, il contrario non è vero. Si ottiene il più rappresentabile double, assumendo la modalità di arrotondamento predefinita). Comunque, questo era un modo totalmente valido per porre la domanda; Non l'ho letto attentamente>. <
Peter Cordes,

"Si noti che l'endianness non influisce sul risultato, quindi è possibile utilizzare in modo sicuro l'effettiva rappresentazione interna della macchina dei valori di doppia precisione per calcolare l'output." a meno che la tua macchina non usi il formato IEEE in virgola mobile ...
Jerry Jeremiah,

Risposte:


8

MATL , 5 byte

3Z%Bz

Provalo online!

Traslitterazione esatta della mia risposta MATLAB. Si noti che input e output sono impliciti. -2 byte grazie 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

33

linguaggio macchina x86_64 (Linux), 16 byte

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

Accetta un singolo parametro intero a 64 bit in RDI, lo converte in un valore a virgola mobile in XMM0, memorizza nuovamente quei bit RAXe quindi calcola il peso di hamming di RAX, lasciando il risultato in RAXmodo che possa essere restituito al chiamante.

Richiede un processore che supporti le POPCNTistruzioni, che sarebbe Intel Nehalem, AMD Barcelona e microarchitettura successive.

Per provarlo online! , compilare ed eseguire il seguente programma 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));
}

2
+1, lo strumento giusto per il lavoro! Questa potrebbe essere l'unica volta in cui x86 può legittimamente competere con le lingue del golf o battere Jelly. :)
DJMcMayhem

2
Ew, sintassi AT&T? È possibile utilizzare objdump -drwC -Mintelper disassemblare nella sintassi Intel. Se in un registro fosse presente un puntatore che è possibile utilizzare per archiviare / ricaricare, è possibile salvare i byte con movaps [rsi], xmm0/ popcnt rax, [rsi]. (i movaps sono solo 3 byte, 2 più brevi di movq.) Ma questo non aiuta qui, perché [rsp-24]richiede 2 byte extra (SIB dall'uso di RSP come base, più disp8). E quei byte extra sono necessari sia nello store che nella ricarica. Oh bene, pensavo di aver visto un salvataggio, ma no: /
Peter Cordes,

Ho salvato 4 byte con una convenzione di chiamata personalizzata . Oppure salva ancora 2 byte con la stessa convenzione di chiamata di questo, usando le istruzioni x87.
Peter Cordes,

1
@DJMcMayhem: forse non è l'unica volta. Non ci sono ancora risposte in lingua golf sulla sfida Extreme Fibonacci (stampa le prime 1000 cifre di Fib (1 miliardo) e la mia risposta al codice macchina x86 (105 byte veloci o 101 byte che corre in 5 minuti anziché 1 minuto) non è molto più grande di alcune delle altre risposte e sono tutte in lingue con numeri interi di precisione estesa integrati.
Peter Cordes,

2
O una sfida più semplice, (e senza un requisito prestazionale), chroma-key che unisce una matrice di numeri interi . La mia risposta in codice macchina è metà della lunghezza della risposta Pyth.
Peter Cordes,

11

C (gcc) , 82 68 byte

9 byte grazie a Neil.

male a virgola mobile hacking a livello di bit

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

Provalo online!


Sapevo che saresti stato il primo, non mi aspettavo la lingua MrGreen
Luis Mendo,

@LuisMendo Ho solo pensato che sarebbe stato comodo in quella lingua ... Non conosco altre lingue che possano farlo
Leaky Nun,

2
Salva 9 byte spostando l'altro modo: ... ;long l=... ;l*=2;)s+=l<0;...
Neil,

1
Ciò ovviamente richiede un'implementazione in C con 64 bit long. Funziona su Linux x86-64, ma fallirebbe su Windows. Suggerirei di dire "gcc con 64 bit long", poiché gcc gira su molte piattaforme, molte delle quali con ABI differenti.
Peter Cordes,

1
@ Il commento di Peter è il motivo per cui ho aggiunto "LP64" in una modifica. Ho anche riorganizzato l'altro testo in quello che pensavo fosse un ordine più logico. Immagino che questa modifica non ti sia piaciuta e che sia stata ripristinata, ma LP64 è un termine standard che descrive l'ABI in cui long e pointer sono valori a 64 bit (rispetto a ILP64, dove gli ints sono anche a 64 bit o LLP64, come usato su Windows dove solo long long e pointer sono 64 bit e long sono ancora 32 bit). Forse avrei dovuto aggiungere ulteriori spiegazioni o un collegamento in linea al pertinente articolo di Wikipedia.
Cody Gray,

8

Python 3 , 72 71 byte

1 byte grazie a Lynn.

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

Provalo online!

Spiegazione

Il formato binary64 è composto da tre componenti:

  • il primo bit è il bit del segno, ovvero 1se il numero è negativo
  • i successivi 11 bit memorizzano l'esponente con l'aggiunta di 1023
  • i successivi 52 bit memorizzano il significato, o la mantissa.

n and(…)-(n>0)un byte è più corto, no?
Lynn,

O int-> float, o qualsiasi altro float, del resto.
user2357112 supporta Monica il

8

C (gcc) , 47 byte

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

Questo non è portatile; è stato testato con gcc 7.1.1 su x86_64 con Linux, senza flag di compilazione.

Provalo online!


1
L'input deve essere un numero intero. O va bene lasciare che il chiamante lo gestisca mediante conversione implicita di longal doublesito di chiamata?
Peter Cordes,

1
Inoltre, basandosi su un colpo di fortuna di comportamento del compilatore per accadere a lasciare nin raxcon codice non-ottimizzato è piuttosto scadente. Si interrompe se si abilita -O3, quindi non è solo gcc in generale, è gcc su x86-64 con 64-bit longcon ottimizzazione disabilitata. Se metti tutti questi requisiti nella tua risposta, voterei. Suppongo che ci siano piattaforme che supportano gcc a 64 bit longma che lasciano il popcountlrisultato in un registro diverso dal registro del valore restituito.
Peter Cordes,

1
Ho preso il numero intero in senso matematico. Ho aggiunto le specifiche dei miei ambienti di test, dato che non sono sicuro che bastino gcc, x86-64 e 64-bit. Detto questo, almeno su x86, le funzioni senza ritorno funzionano con gcc (e tcc) il più delle volte.
Dennis,

Sì, stavo solo rileggendo la domanda, e sono d'accordo che accettare l'arg come doubledovrebbe andare bene. Non dice nulla sulla necessità che la funzione lo accetti nel formato base2. E sì, diverse versioni di gcc potrebbero emettere codice diverso, quindi anche questo è importante. (Fatto curioso : senza -mpopcnt, gcc non userà l' popcntinsn ed emetterà una sequenza di istruzioni per emularlo. Alcune architetture non hanno affatto un'istruzione popcnt, quindi __builtin_popcountldeve sempre usare una sequenza di insn)
Peter Cordes

Sì, molte (la maggior parte?) __builtin_*Funzioni hanno versioni legacy per evitare di generare istruzioni illegali. -march=nativeutilizza popcntqsolo se è disponibile.
Dennis,


6

C (gcc), 63 byte

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

Questa soluzione si basa sulla risposta di @ LeakyNun, ma poiché non vuole migliorare la propria risposta, sto pubblicando qui una versione più giocata.

Provalo online


2
Dubito fortemente che nessuno non voglia migliorare la propria risposta.
Mr. Xcoder,

1
@ Mr.Xcoder. Ok, lo terrò qui finché non modificherà la sua risposta. Se non vuole modificare, questo rimarrà qui. Ho pubblicato questo miglioramento come commento alla sua risposta e l'ha respinto.

1
Penso che l'input debba essere un tipo intero e non reale.
ceilingcat,

3
@ThePirateBay Non ho visto il tuo commento sulla mia risposta e non lo vedo ancora adesso.
Leaky Nun,

9
La decisione di suggerire miglioramenti o pubblicare la tua risposta è tua, ma 6 minuti sono quasi un'ora .
Dennis,

5

C #, 81 70 68 byte

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

Risparmia 11 byte grazie a @Leaky Nun.
Salvato 2 byte grazie a @Neil.

Provalo online! Utilizza al System.BitConverter.DoubleToInt64Bitsposto del unsafecodice in quanto non sono riuscito a far funzionare TIO con esso.

Versione completa / formattata:

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();
        }
    }
}

for(;l!=0;l*=2)e non avrai bisogno del ternario
Leaky Nun,

@LeakyNun Grazie mi stavo grattando la testa per anni.
TheLethalCoder,

Puoi usare s-=l>>31?
Neil,

@Neil Non sembra funzionare. Presumo che intendi sostituire s+=l<0?1:0?
TheLethalCoder,

Colpa mia; lè lungo, quindi ha bisogno s-=l>>63?
Neil,


4

JavaScript (ES6), 81 80 77 byte

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

Modifica: salvato 1 byte grazie a @Arnauld. Salvato 3 byte grazie a @DocMax.


Potresti fare g(i^i&-i,x++)per -1 byte?
Arnauld,

@Arnauld Mi chiedevo se ci fosse un pizzico da golfista, grazie per averlo trovato!
Neil,

1
-3 in più se si sostituisce new Float64Array([n])conFloat64Array.of(n)
DocMax

4

codice macchina x86-64, 12 byte per l' int64_tinput

6 byte per l' doubleinput

Richiede l' popcntestensione ISA ( CPUID.01H:ECX.POPCNT [Bit 23] = 1).

(O 13 byte se la modifica dell'arg sul posto richiede la scrittura di tutti i 64 bit, invece di lasciare immondizia nella parte superiore 32. Penso che sia ragionevole sostenere che il chiamante vorrebbe probabilmente caricare solo i 32b bassi comunque e x86 zero -estensione da 32 a 64 implicitamente ad ogni operazione a 32 bit. Tuttavia, impedisce al chiamante di fare add rbx, [rdi]o qualcosa del genere.)

le istruzioni x87 sono più brevi del più ovvio SSE2 cvtsi2sd/ movq(usato nella risposta di @ ceilingcat ) e una [reg]modalità di indirizzamento ha le stesse dimensioni di un reg: solo un byte mod / rm.

Il trucco era trovare un modo per far passare il valore in memoria, senza bisogno di troppi byte per le modalità di indirizzamento. (es. passare lo stack non è eccezionale.) Fortunatamente, le regole consentono argomenti di lettura / scrittura o argomenti di output separati , quindi posso solo convincere il chiamante a passare un puntatore alla memoria che mi è permesso scrivere.

Callable da C con la firma: void popc_double(int64_t *in_out); sono validi solo i 32b bassi del risultato, il che è forse strano per C ma naturale per asm. (Per risolvere questo problema è necessario un prefisso REX nell'archivio finale ( mov [rdi], rax), quindi un altro byte.) Su Windows, passare rdia rdx, poiché Windows non utilizza l'ABI System V x86-64.

Elenco della NASM. Il collegamento TIO ha il codice sorgente senza lo smontaggio.

  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

Provalo online! Include un_startprogramma di test che gli passa un valore ed esce con exit status = valore di ritorno popcnt. (Apri la scheda "debug" per vederlo.)

Anche il passaggio di puntatori di input / output separati funzionerebbe (rdi e rsi nell'ABI SystemV x86-64), ma non possiamo ragionevolmente distruggere l'input a 64 bit o giustificare altrettanto facilmente la necessità di un buffer di output a 64 bit mentre si scrive solo il basso 32b.

Se vogliamo sostenere che possiamo prendere un puntatore all'intero di input e distruggerlo, restituendo l'output in rax, quindi semplicemente omettere il mov [rdi], eaxda popcnt_double_outarg, portandolo a 10 byte.


Alternativa senza sciocchi trucchi convenzionali per le chiamate, 14 byte

usa la pila come spazio di lavoro, con pushper arrivarci. Utilizzare push/ popper copiare i registri in 2 byte anziché 3 per mov rdi, rsp. (ha [rsp]sempre bisogno di un byte SIB, quindi vale la pena spendere 2 byte da copiare rspprima di tre istruzioni che lo utilizzano.)

Chiama da C con questa firma: 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.

Accettare input in doubleformato

La domanda dice solo che è un numero intero in un certo intervallo, non che deve essere in una rappresentazione di numero intero binario base2. Accettare l' doubleinput significa che non ha più senso usare x87. (A meno che non si usi una convenzione di chiamata personalizzata in cui le doubles vengono passate nei registri x87. Quindi archiviare nella zona rossa sotto lo stack e popcnt da lì.)

11 byte:

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

Ma possiamo usare lo stesso trucco pass-by-reference di prima per creare una versione a 6 byte: int pcd(const double&d);

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

6 byte .



3

MATLAB, 36 byte

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

Utilizzando il fatto che de2binon è solo più breve di dec2bin, ma fornisce anche un risultato in uno e zero anziché in ASCII 48, 49.


3

Java (64, 61, 41 byte)

Completamente semplice usando la libreria standard (Java SE 5+):

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

Contributo di Kevin Cruijssen (Java SE 5+):

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

Contributo di Kevin Cruijssen (Java SE 8+, funzione lambda):

n->n.bitCount(Double.doubleToLongBits(n))

Ben fatto! :-)
Leaky Nun,

1
Bella risposta, +1 da parte mia. Puoi giocare a golf tre byte prendendo il parametro come Long ne usando n.bitCount(...)invece di Long.bitCount(...). Inoltre, se si utilizza Java 8+, è possibile giocare a golf n->n.bitCount(Double.doubleToLongBits(n))( 41 byte )
Kevin Cruijssen,

2

Solo per provare un approccio diverso, più sicuro di TheLethalCoder , ho pensato a questo (è un peccato che C # abbia nomi di metodi così lunghi):

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

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

Provalo online!

Il conteggio dei byte include 13 byte per using System;. Per prima cosa ho bisogno di convertire il file doublea longcon la stessa rappresentazione binaria, quindi posso convertirlo in un file binario string, quindi conto le 1s semplicemente dividendo la stringa e contando le sottostringhe meno 1.


Buona alternativa ma è necessario includere il usingnel conteggio dei byte.
TheLethalCoder

Utilizzare LINQ per 95 byte solo un paio di più: namespace System.Linq;{d=>Convert.ToString(BitConverter.DoubleToInt64Bits(d),2).Count(c=>c>48)}. Anche se non l'ho provato, dovrebbe funzionare.
TheLethalCoder,

@TheLethalCoder funziona, ma ho cercato di evitare Linq, quindi non ho dovuto aggiungere una seconda usingdirettiva.
Charlie,

1
Quando aggiungi il secondo, namespaceè utile. Ma sì, in questo caso evitare Linq era leggermente più economico. Volevo solo commentare con il suo approccio nel caso avessi qualche idea su come accorciarlo per risparmiare byte.
TheLethalCoder,

@TheLethalCoder, Sum(c=>c&1)è più corto. OppureSum()-768
Peter Taylor,


1

dc, 79 byte

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

L'output viene lasciato in cima allo stack.
Aggiungerò una spiegazione più tardi.

Provalo online!

Si noti che i numeri negativi sono preceduti da _, non -.



1

C, 67 byte

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

codice di controllo e risultati

#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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.