フロート754からハミング


29

入力として(-2 52)から(2 52kの範囲の整数が与えられます。れているようによく知られている、この範囲内の整数で正確に倍精度浮動小数点値として表すことができます。-45035996273704964503599627370496

次のことを行う必要があり、出力ハミング重みのエンコーディングの(1の数)kbinary64形式。これは、符号に1ビット、指数に11ビット(オフセットでエンコード)、仮数に52ビットを使用します。詳細については、上記のリンクを参照してください。

例えば、数22として表され

0 10000000011 0110000000000000000000000000000000000000000000000000

あるため5、出力は5です。

エンディアンは結果に影響しないため、マシンの実際の倍精度値の内部表現を安全に使用して出力を計算できます。

追加のルール

テストケース

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

1
関数はbinary64、必要に応じて既に浮動小数点形式の入力を受け入れることができるようにしていますか?一部の人々(最初は私も含めて)は、関数がCのような整数型として入力を受け入れることを要求すると質問を解釈していましたlong。Cでは、を呼び出すときと同じように、言語があなたのために変換されると主張できますsqrt((int)foo)。しかし、x86マシンコードasmの回答(codegolf.stackexchange.com/a/136360/30206や私のようなもの)がいくつかあり、どちらも64ビット整数入力を受け入れなければならないと仮定していました。binary64値を受け入れると、5バイト節約されます。
ピーターコーデス

もしそうなら、制限された範囲に関するすべてのものは、誰かがタイプパニングの代わりにバイナリ64ビットパターンへの変換をハックしたい場合に備えていますか?または、型のパンニングのない言語の場合は?うーん、興味深い課題は、aの指数と仮数をbinary64base2整数として追加することです。とにかくそれらを別々に処理する必要がある場合は、type-punとループ以外の何かをする価値があるかもしれません。
ピーターコーデス

2
@PeterCordesはい、浮動小数点数の形式で入力できます。限られた範囲は、浮動小数点表現が正確であることを確認することです
ルイスMendo

はい、ありがとう。すべてのdoubleが整数であるlongとは限らないため、aをとる関数を作成するオプションを残したいと思うので、単にbinary64とは言えませdoubleん。ただし、整数値doubleのsはすべてlong、の制限まで変換および逆変換できますlong。(指摘したように、その逆は真ではありません。doubleデフォルトの丸めモードを仮定すると、最も近い表現可能になります)。とにかく、これは質問を設定する完全に有効な方法でした。私はちょうど>慎重にそれを読んでいない<。
ピーター・コルド

「エンディアンネスは結果に影響しないため、マシンの実際の倍精度値の内部表現を安全に使用して出力を計算できます。」あなたのマシンは、IEEE浮動小数点形式を使用していない場合を除き...
ジェリーエレミヤ

回答:


8

MATL、5バイト

3Z%Bz

オンラインでお試しください!

MATLAB回答の正確な音訳。入力と出力は暗黙的であることに注意してください。ルイスメンドーのおかげで-2バイト。

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

x86_64マシン言語(Linux)、16バイト

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

単一の64ビット整数パラメータを受け入れ、RDIそれを浮動小数点値in に変換しXMM0、それらのビットを格納しRAX、のハミング重みを計算しRAX、結果を残しRAXて呼び出し元に返すことができます。

POPCNT命令をサポートするプロセッサー(Intel Nehalem、AMD Barcelona、およびそれ以降のマイクロアーキテクチャー)が必要です。

するには、オンラインでそれを試してみてください!、次の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、仕事に最適なツール!これは、x86がゴルフ言語と合法的に競い合う、またはJellyを打ち負かすことができる唯一の時間かもしれません。:)
DJMcMayhem

2
Ew、AT&T構文?objdump -drwC -MintelIntel構文で逆アセンブルするために使用できます。格納/再読み込みに使用できるレジスタにポインタがある場合、movaps [rsi], xmm0/でバイトを保存できますpopcnt rax, [rsi]。(movapsは3バイトのみで、2つはmovqよりも短いです。)しかし、[rsp-24]2バイトの余分なバイト(RSPをベースとして使用するSIBとdisp8)を必要とするため、ここでは役に立ちません。また、ストアとリロードの両方でこれらの余分なバイトが必要です。まあ、私は貯金を見たと思ったが、いや::
Peter Cordes

カスタム呼び出し規約で4バイト保存しました。または、x87命令を使用して、これと同じ呼び出し規則で2バイトを節約します。
ピーターコーデス

1
@DJMcMayhem:たぶん唯一の時間ではありません。Extreme Fibonacciチャレンジに関するゴルフ言語の回答はまだありません(Fib(10億)の最初の1000桁を印刷し、x86マシンコードの回答(105バイト高速、または1分ではなく5分で実行される101バイト)他の回答のいくつかよりもはるかに大きいではなく、彼らが内蔵され、拡張精度の整数と言語のすべてだ。
ピーター・コルド

2
または、より単純な課題(およびパフォーマンス要件なし)、整数の配列をクロマキーブレンドします。私のマシンコードの回答は、pyth回答の長さの半分です。
ピーターコーデス

11

C(gcc)82 68バイト

ニールのおかげで9バイト。

邪悪な浮動小数点ビットレベルのハッキング

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

オンラインでお試しください!


私はちょうど言語:-D期待していなかった、あなたが最初だろう知っていた
ルイスMendo

@LuisMendo私はちょうどそれがその言語で便利だろうと思った...私はこれを行うことができ、他の言語を知らない
漏れ修道女

2
他の方法でシフトして9バイトを節約します;long l=。... ;l*=2;)s+=l<0;...
ニール

1
もちろん、これには64ビットのC実装が必要ですlong。x86-64 Linuxでは動作しますが、Windowsでは失敗します。longgccは多くのプラットフォームで実行されているため、「64ビットのgcc」を使用することをお勧めします。その多くは異なるABIを使用しています。
ピーターコーデス

1
@Peterのコメントが、編集に「LP64」を追加した理由です。また、他のテキストをより論理的な順序だと思ったものに再配置しました。その変更が気に入らず、ロールバックされたと思いますが、LP64は、longとポインターが64ビット値であるABIを表す標準用語です(ILP64はintも64ビット、またはLLP64であり、 Windowsで使用されているlong longとポインタのみが64ビットで、longは32ビットのままです)。おそらく、さらに説明を追加するか、関連するウィキペディアの記事へのインラインリンクを追加する必要がありました。
コーディグレー

8

Pythonの372の 71バイト

Lynnのおかげで1バイト。

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

オンラインでお試しください!

説明

binary64形式は、3つのコンポーネントで構成されています。

  • 最初のビットは符号ビットで1、数値が負の場合
  • 次の11ビットには、1023が追加された指数が格納されます
  • 次の52ビットには、仮数または仮数が格納されます。

n and(…)-(n>0)バイトは短いですか?
リン

または、int-> float、またはすべてのfloat、その点については。
user2357112は、モニカをサポートします

8

C(gcc)、47バイト

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

これは移植性がありません。Linuxを実行するx86_64上のgcc 7.1.1で、コンパイラフラグなしでテストされました。

オンラインでお試しください!


1
入力は整数でなければなりません。それともの暗黙的な変換であること、発信者のハンドルを聞かせしても大丈夫であるlongdouble呼び出しサイトでは?
ピーターコーデス

1
また、最適化さnraxていないコードを残すためにたまたまコンパイラの振る舞いに頼ることは、非常に安っぽいです。を有効にすると壊れる-O3ので、一般的なgccだけでなくlong、最適化が無効になっている64ビットのx86-64上のgcc です。これらすべての要件を答えに入れていただければ、賛成です。64ビットのgccサポートがあるプラットフォームがあると思いますlongが、popcountl結果が戻り値レジスタ以外のレジスタに残ることがあります。
ピーター・コーデス

1
数学的な意味で整数を取りました。テスト環境の仕様を追加しました。gcc、x86-64、および64ビットのlongで十分かどうかわからないからです。とはいえ、少なくともx86では、リターンレス関数はgcc(およびtcc)で頻繁に動作します。
デニス

ええ、私は質問を読み直しただけで、引数をaとして受け入れることは問題ないことに同意しdoubleます。関数がbase2形式でそれを受け入れることを要求することについては何も述べていません。そして、はい、gccの異なるバージョンは異なるコードを出力する可能性があるため、それも重要です。(面白い事実:なしでは-mpopcnt、gccはpopcntinsnを使用せず、それをエミュレートする一連の命令を発行します。一部のアーキテクチャにはpopcnt命令がまったくないため、__builtin_popcountl常にいくつかのinsnシーケンスを使用する必要があります)
Peter Cordes

はい、多くの(ほとんどの?)__builtin_*関数には、不正な命令の生成を避けるためのレガシーバージョンがあります。-march=native使用popcntq可能な場合にのみ使用します。
デニス


6

C(gcc)、63バイト

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

このソリューションは@LeakyNunの答えに基づいていますが、彼は自分の答えを改善したくないので、ここにもっとゴルフ版を投稿します。

オンラインで試す


2
答えを改善たくない人はいないと思います。
ミスターXcoder

1
@ Mr.Xcoder。わかりました、彼が彼自身の答えを編集するまで私はこれをここに保ちます 彼が編集したくない場合、これはここに残ります。私は彼の答えに対するコメントとしてこの改善を投稿し、彼はそれを拒否しました。

1
入力は実数ではなく整数型である必要があると思います。
ceilingcat

3
@ThePirateBay私の答えに対するあなたのコメントは見ませんでしたが、今はまだ見ていません。
リーキー修道女

9
改善を提案するか、独自の回答を投稿するかはあなた次第ですが、6分は約1時間ではありません。
デニス

5

C#、81 70 68バイト

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

@Leaky Nunのおかげで11バイト節約できます。
@Neilのおかげで2バイト節約されました。

オンラインでお試しください!TIOで動作させることができなかったためSystem.BitConverter.DoubleToInt64Bitsunsafeコードの代わりに使用します。

完全/フォーマット済みバージョン:

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)そして、あなたは三項を必要としません
リーキー修道女

@LeakyNunありがとうございます。
TheLethalCoder

使用できますs-=l>>31か?
ニール

@Neilは機能していないようです。私はあなたが交換するつもりだと思いますs+=l<0?1:0か?
TheLethalCoder

私の悪い; l長いので、それが必要s-=l>>63ですか?
ニール

4

Python 2、69バイト

-12バイト、@ ASCIIのみのおかげ

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

オンラインでお試しください!



1
あなたのアプローチ、76バイトのゴルフ、私はかかわらず、ASCIIのみのアプローチをお勧めします
ミスターXcoder

Mr.Xcoder @ !バイト順序が問題でここにいないので必要とされていません
ASCIIのみ


@ ASCII-only解凍されたものをパックします。ありがとう:D
デッドポッサム

4

JavaScript(ES6)、81 80 77バイト

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

編集:@Arnauldのおかげで1バイト保存されました。@DocMaxのおかげで3バイト節約されました。


g(i^i&-i,x++)-1バイトでできますか?
アーナルド

@アーナウルド私はゴルファーのビットの回転があったかどうか疑問に思いました、それを見つけてくれてありがとう!
ニール

1
-3あなたは置き換えるもっとあればnew Float64Array([n])Float64Array.of(n)
DocMax

4

x86-64マシンコード、int64_t入力用12バイト

double入力用に6バイト

popcntISA拡張CPUID.01H:ECX.POPCNT [Bit 23] = 1)が必要です。

(または、引数をインプレースで変更する場合、上位32ビットにガベージを残すのではなく、すべての64ビットを書き込む必要がある場合は13バイトです。 -すべての32ビット操作で暗黙的に32から64に拡張します。それでも、呼び出し元の実行add rbx, [rdi]などを停止します。

x87命令は、より明白なSSE2 cvtsi2sd/ movq@ceilingcatの回答で使用)よりも短く、[reg]アドレス指定モードはaと同じサイズregです:ちょうどmod / rmバイト。

トリックは、アドレス指定モードに多すぎるバイトを必要とせずに、値をメモリに渡す方法を考え出すことでした。(たとえば、スタックを渡すことはそれほど素晴らしいことではありません。)幸いなことに、ルールでは読み取り/書き込み引数、または個別の出力引数が許可されているので、呼び出し元に、書き込みが許可されているメモリへのポインターを渡してもらうことができます。

署名付きでCから呼び出し可能: void popc_double(int64_t *in_out); 結果の下位32bのみが有効です。これは、Cでは奇妙かもしれませんが、asmでは自然です。(これを修正するには、最終ストア(mov [rdi], rax)にREXプレフィックスが必要なので、もう1バイト。)Windowsではx86-64 System V ABIを使用しないため、に変更rdirdxます。

NASMリスト。TIOリンクには、逆アセンブリのないソースコードがあります。

  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

オンラインでお試しください!_start値を渡し、exit status = popcnt戻り値で終了 するテストプログラムが含まれています。(「デバッグ」タブを開いて確認してください。)

個別の入力/出力ポインタを渡すことも機能しますが(x86-64 SystemV ABIのrdiとrsi)、64ビット入力を合理的に破棄することはできません。低い32b。

入力整数へのポインタを取得して破棄し、出力をin raxに戻すことができると主張したい場合は、単にmov [rdi], eaxfromを省略popcnt_double_outargして10バイトにします。


愚かな呼び出し規約トリックのない代替、14バイト

スタックをスクラッチスペースとして使用して、pushそこに到達します。push/ popを使用して、レジスタを3バイトではなく2バイトでコピーしmov rdi, rspます。([rsp]常にSIBバイトが必要なので、それrspを使用する3つの命令の前にコピーするのに2バイトを費やす価値があります。)

この署名を使用してCから呼び出します。 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.

double形式での入力の受け入れ

質問は、それが特定の範囲の整数であり、base2バイナリ整数表現である必要はないというだけです。double入力を受け付けると、x87を使用しても意味がなくなります。(doublesがx87レジスタで渡されるカスタム呼び出し規約を使用しない限り、スタックの下のレッドゾーンに保存し、そこからpopcntします。)

11バイト:

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

ただし、以前と同じ参照渡しのトリックを使用して、6バイトバージョンを作成できます。 int pcd(const double&d);

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

6バイト



3

MATLAB、36バイト

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

de2biだけdec2binでなく、ASCII 48、49ではなく1と0で結果を提供するという事実を使用します。


3

Java(64、61、41バイト)

標準ライブラリ(Java SE 5+)を使用すると完全に簡単です:

int f(long n){Longを返します。bitCountDouble。doubleToLongBits(n));}

Kevin Cruijssen(Java SE 5+)による貢献:

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

Kevin Cruijssenによる貢献(Java SE 8+、ラムダ関数):

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

よくできました!:
リーキー修道女

1
いい答え、私からの+1。パラメータをとしてLong n使用し、のn.bitCount(...)代わりにを使用して、3バイトをゴルフできますLong.bitCount(...)。さらに、Java 8以降を使用している場合は、ゴルフをn->n.bitCount(Double.doubleToLongBits(n))41バイト
ケビンCruijssen

2

別の、より安全なTheLethalCoderのアプローチを試すために、私はこれを思いつきました(残念なことに、C#はそのような長いメソッド名を持っています)。

C#(.NET Core)、76 + 13バイト

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

オンラインでお試しください!

バイトカウントにはの13バイトが含まれますusing System;。まず、私は変換する必要があるdoublelongと同じバイナリ表現を持っていること、そして、私はバイナリに変換することができstring、その後、私は数える1分割によって単に文字列と部分文字列のマイナス1を数えますよ。


素晴らしい代替手段ですがusing、バイトカウントに含める必要があります。
TheLethalCoder

95バイトのLinqを使用するのはもう少しだけですnamespace System.Linq;{d=>Convert.ToString(BitConverter.DoubleToInt64Bits(d),2).Count(c=>c>48)}。私はそれをテストしていませんが、動作するはずです。
TheLethalCoder

@TheLethalCoderは動作しますが、Linqを避けようとしたため、2番目のusingディレクティブを追加する必要はありませんでした。
チャーリー

1
秒を追加すると、namespacesが便利になります。しかし、この場合、Linqを回避するほうが少し安くなりました。バイトを節約するためにそれを短くする方法についてアイデアがあった場合に備えて、このアプローチでコメントしたかっただけです。
TheLethalCoder

@TheLethalCoder Sum(c=>c&1)は短いです。またはSum()-768
ピーターテイラー


1

dc、79バイト

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

出力はスタックの上部に残ります。
後で説明を追加します。

オンラインでお試しください!

負の数がが先行していることに注意してください_ではありません、-



1

C、67バイト

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

制御コードと結果

#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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.