リトルエンディアンから文字列への変換


13

前書き

BMP(ビットマップ)ジェネレーターでの作業中に、数値をリトルエンディアンの16進文字列に変換する問題に直面しています。JavaScriptで作成した関数を次に示しますが、小さなコードでも同様に機能するのではないかと思います

let liEnd= num => num.toString(16).padStart(8,'0').match(/../g).reverse().join``;
console.log(liEnd(304767)) // 304767 dec = 0x4a67f hex

チャレンジ

入力時に32ビットの符号なし整数を受け取り、リトルエンディアンの順序で8桁の16進数文字列を生成する関数を作成します。ジョブを実行するアルゴリズムの例:

  • numbを16進文字列に変換します。例: 304767 -> '4a67f'
  • パディングゼロを追加して、8文字の文字列を取得します。 '0004a67f'
  • 文字列を4つの2文字に分割します。 '00','04','a6','7f'
  • ピースの逆順 '7f','a6','04','00'
  • ピースを結合して結果として返す: '7fa60400'

入力と出力の例

入力番号(または10 ->進数の文字列)はの左側にあり、出力16進数の文字列は右側にあります

2141586432 -> 0004a67f
304767     -> 7fa60400

回答:



6

Python 3、37バイト

lambda n:n.to_bytes(4,"little").hex()

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

算術ベースの再帰的ソリューション(50 49バイト、Python 2でも動作)

f=lambda n,i=4:i*'1'and"%02x"%(n%256)+f(n>>8,i-1)

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

@JonathanAllanのおかげで-1バイト


再帰的なものをPython 2エントリとして提出してください:)
ジョナサンアラン

f=lambda n,i=4:i*'1'and'%02x'%(n%256)+f(n>>8,i-1)バイトを保存:)
ジョナサンアラン

@JonathanAllanありがとう。私はすべてのPython 2トリックに精通しているわけではありませんが、どのように短くすることができるかわかりません。
ジョエル

それはありませんが、py 2では37は動作しません
ジョナサンアラン

うん。これらのビルトインの一部はPython 3専用です。
ジョエル

6

R54 53バイト

format.hexmode(scan()%/%256^(0:3)%%256%*%256^(3:0),8)

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

2文字の各グループは、実際には256を基数とする16進数表現ですscan()%/%256^(0:3)%%256。4桁を逆にして256を基数に変換し、 ...%*%256^(3:0)それらを単一の整数として結合し、format.hexmode(...,8)その数字を8桁の16進表現に変換します。


5

JavaScript(ES7)、 59  57バイト

文字列操作。

n=>(n+2**32).toString(16).match(/\B../g).reverse().join``

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

どうやって?

最初にn+232を16進数に変換して、先頭の0がすべて含まれるようにします。

(304767 + 2**32).toString(16) // --> '10004a67f'

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

私たちは、正規表現を使用して/\B../gリード無視して、2桁のすべてのグループに一致するように1のおかげで\B(非単語境界を)。

'10004a67f'.match(/\B../g) // --> [ '00', '04', 'a6', '7f' ]

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

私たちreverse()join()、最終的な文字列を取得します。


JavaScript(ES6)、61バイト

再帰関数。

f=(n,k=4)=>k?[(x=n&255)>>4&&'']+x.toString(16)+f(n>>8,k-1):''

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


⭐-あなたは素敵な答えのために星を取得します-私はそれが好きです、短いがまだきれいで、「人間が再利用可能」:)
カミルKiełczewski19年


5

C#(Visual C#Interactive Compiler)、54バイト

x=>$"{(x=x>>16|x<<16)>>8&16711935|(x&16711935)<<8:x8}"

@PeterCordesのおかげで4バイト節約

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

説明

x=>                                                    //Lambda taking in an uint
     (x=x>>16|x<<16)                                   //Swap the first two and the last two bytes of the uint (0x7fa60400 -> 0x04007fa6)
                    >>8&16711935|(x&16711935)<<8       //Swap each pair of bytes in every group of 2 bytes (0x04007fa6 -> 0x0004a67f)
  $"{                                           :x8}"  //Format as hex string, padded with leading zeroes to length 8

4278255360マスクする前にシフトした場合、マスク定数を167119350xff00ff)に縮小できますか?それとも余分なカレンがかかりますか?また、そうでない場合、同じ長さですが、人間にとってはるかに意味があります。0xff00ff00
ピーター

@PeterCordesまた、>>よりも優先順位が高いため、ブラケットを削除できるという利点もありますが、&合計で4バイト節約されます。ありがとう!
無知の

涼しい。「説明」セクションでは、定数を16進数で記述することをお勧めします。
ピーター

4

Japt -P、10 バイト

sG ùT8 ò w

それを試してみてください

sG ùT8 ò w     :Implicit input of integer
s              :Convert to string
 G             :  In base-16
   ù           :Left pad
    T          :  With 0
     8         :  To length 8
       ò       :Split into 2s
         w     :Reverse
               :Implicitly join and output

何を-Pするの?
SSアン

🚀あなたの答えは一番上にあります(説明を追加できますか?)
カミルKiełczewski19年

JL2210 @ ドキュメントから:「-P:出力配列である場合、ないセパレータを出力(すなわちと結合P)。」。したがって、このフラグは、バイトを節約するための明示的な結合ではなく暗黙的な結合用です。:)
ケビンクルーッセン

2
@KamilKiełczewski、説明が追加されました。
シャギー



3

C(gcc)エンディアンに依存せず、標準ライブラリなし、92 91バイト

h(n)1桁の整数-> 16進ヘルパー関数です。
f(x,p)整数とchar[8]ポインターを取ります。結果は8バイトのcharデータです。(呼び出し元がそうしない限り、0で終了しません。)

前提:ASCII文字セット。2の補数なintので、右シフトは最終的に符号ビットを引き下げます。上位ビットが設定されている場合、a uint32_tを変換してintもビットパターンは変更されません。 int少なくとも32ビットです。(より広い1の補数または符号マグニチュードC実装で動作させることがあります)。

非仮定:実装のバイト順またはの署名についてのすべてchar

i;h(n){n&=15;return n>9?n+87:n+48;}f(x,p)char*p;{for(i=5;--i;x>>=8)*p++=h(x>>4),*p++=h(x);}

オンラインでお試しください!printf("%.8s\n", buf)0で終了せずに出力バッファーを印刷 するために使用するテスト呼び出し元を含みます。

ゴルフをしていない:

int h(n){n&=15;return n>9 ? n+'a'-10 : n+'0';}      // single digit integer -> hex

int i;
void ungolfed_f(x,p)char*p;{
    for(i=5; --i; x>>=8)   // LS byte first across bytes
        *p++=h(x>>4),      // MS nibble first within bytes
        *p++=h(x);
}

n&=15;内部h(x)で行うことは損益分岐点です。&15両方の呼び出しサイトで低ニブルを分離するためにそれぞれ6バイト対3バイト。

,はシーケンスポイント(または現代用語では同等)であるため*p++= stuff,演算子で区切られている場合、1つのステートメントで2回実行しても安全です。

>>符号付き整数は、算術または論理として実装定義されます。GNU Cは、算術2の補数として定義しています。しかし、2の補数のマシンでは、シフトインされた0や符号ビットのコピーを見ることはないため、それは実際には重要ではありません。元のMSBは、最終的には変更されずに下位バイトに入ります。これは符号/大きさの場合ではなく、1の補数についてはわかりません。

したがって、これは2の補数のC実装にのみ移植可能です。(または、ここint、より広いビット31は、大きさのほんの一部であるので、32ビットより。)符号なし- >符号付き変換もそう、負の整数のビットパターンをmunges &15intのみ2の補数に元の符号なしの値のニブルを抽出することになります。繰り返しますが、32ビットよりint広い場合を除き、すべての入力は負ではありません。

ゴルフバージョンには、ボイド以外の機能の終わりからUBが落ちることがあります。voiddefaultの代わりに値を宣言しないようにするために、値を返さないようにしますint。最新のコンパイラは、最適化を有効にするとこれを破ります。


動機:私はx86またはARM Thumb asmの回答を検討していましたが、Cで手動で行うのは楽しいかもしれません。おそらくコンパイラーが生成したasmを出発点として。参照/programming/53823756/how-to-convert-a-number-to-hexをのみ、2命令(しかし、ニーズがvpmultishiftqbとvpshufbためのベクターを、コントロールのAVX512VBMIバージョンを含む、スピード効率に優れたx86 ASMのゴルフには向かないでしょう)。通常、SIMDがリトルエンディアンx86の印刷順序にバイトリバースするために余分な作業が必要になるため、このバイトリバース16進出力は実際には通常よりも簡単です。


他のアイデア

char*リトルエンディアンのC実装(x86やARMなど)で、整数を参照で取得し、でそのバイトをループ処理することを検討しました。しかし、私はそれが多くを救うとは思わない。

sprintf一度に1バイト、ゴルフの後に64バイトを行うために使用:

int i;
void f(x,p)char*p;{
        for(i=4;sprintf(p,"%.2x",x&255),--i;x>>=8)
                p+=2;
}

しかし、printfに似た関数を使用している場合は、バイトスワップして@ JL2210のanswerの%xような全体のprintfを実行することもできます


⭐-あなたは素敵な答えのために星を得る
カミルKiełczewski19年

3

x86 SIMDマシンコード(AVX512-VBMI)、36バイト

(16バイトは16進数のルックアップテーブルです)

これは、整数を受け取り、呼び出し元が必要な場所に格納するために、xmm08バイトのASCII文字データを返す関数ですxmm0。(たとえば、属性バイトとインターリーブした後のビデオメモリ、構築中の文字列など)

Cから__m128i retval = lehex(_mm_cvtsi32_si128(x))、x86-64 System Vの呼び出し規約、またはMS Windows と同様に呼び出しますvectorcall

# disassembly with machine-code bytes (the answer) and NASM source code.
0000000000401000 <lehex>:
  401000:       c5 f1 72 d0 04          vpsrld      xmm1, xmm0, 4         ; AVX1
  401005:       c5 f1 60 c8             vpunpcklbw  xmm1, xmm1, xmm0      ; AVX1
  401009:    62 f2 75 08 8d 05 01 00 00 00 vpermb  xmm0, xmm1, [rel .hex_lut]
  401013:       c3                      ret    

0000000000401014 <lehex.hex_lut>:
  401014:     30 31 ...  61 62 ...     .hex_lut:  db "0123456789abcdef"

合計= 0x24 = 36バイト。

数値を16進数に変換する方法をご覧くださいこれがどのように機能するかについては。 (shift / punpckのSSE2は、vpermb必要な作業を保存しpshufbます。SSE2/ SSSE3の代わりにAVX1もmovapsレジスタコピーを回避します。)

punpcklbwソースオペランドをこの順序で使用すると、最下位バイト要素の下位入力バイトの最上位ニブル、最下位ソースバイトの最下位ニブルが得られることに注意してください。(SOの回答でbswapは、入力にa を使用して、SSE2のみの標準印刷順序で結果を取得しますが、ここではその順序が必要です。各バイト内の下位要素の高いニブルですが、リトルエンディアンのバイト順序です)。

より多くのデータ定数があれば、アドレス指定モードmov edx, imm32を使用して、[rdx+16]または任意のアドレス指定モードを使用して、アドレス指定モードのスペースを節約できます。またはvpbroadcastb xmm0, [rdx+1]

しかし、16バイトの16進LUT + vpermbn>9 : n+'a'-10 : n+'0'条件を実装するよりも優れていると思います。3つの定数とAVX512BWバイトマスク(maskと比較vpaddb、merge-masked vpaddb)、またはAVX1またはSSE2以上の少なくとも3つの命令が必要です。(そのSSE2バージョンについては、SOで数値を16進数に変換する方法を参照してください)。また、各AVX512BW命令は少なくとも6バイト長(4バイトEVEX +オペコード+ modrm)で、アドレッシングモードでの変位を伴います。

実際には、比較の前に、(または4バイトのブロードキャストメモリオペランドを含むEVEX)で高いゴミをクリアする必要があるため、少なくとも4つの命令が必要です。そして、それぞれに異なるベクトル定数が必要です。AVX512にはブロードキャストメモリオペランドがありますが、32ビット以上の要素のみです。たとえば、EVEXの最後のオペランドはのみであり、そうではありません。(Intelのロードポートは、ロードuopの一部として32ビットおよび64ビットのブロードキャストのみを無料で行うことができるため、Intelはそれを反映するようにAVX512BWを設計し、バイトまたはワードブロードキャストメモリオペランドをエンコードすることはできません。 dwordブロードキャストを行うと、定数を4バイトに圧縮できます:/。andpsvpanddvpaddbxmm3/m128xmm3/m128/m8bcst

私が使用した理由AVX512VBMIvpermb代わりにSSSE3のは/ AVX1はpshufb二つあります。

  • vpermbセレクターの上位ビットを無視します。 (v)pshufb制御ベクトルの上位ビットに応じてバイトをゼロにし、ニブルを実際に分離するために追加pandまたは必要とするでしょうandps。XMM / 16バイトサイズでvpermbは、シャッフルコントロール要素の下位4ビット、つまり[3:0]操作」セクションの Intelの表記のビットのみを調べます。
  • vpermbシャッフルされるデータ(ルックアップテーブル)をメモリオペランドとして使用できます。 (v)pshufbのxmm / memオペランドはシャッフル制御ベクトルです。

AVX512VBMIはCannonLake / Ice Lakeでのみ使用できるため、これをテストするには、おそらくインテルのSDEのようなシミュレーターが必要です。


⭐-あなたは素敵な答えのために星を得る
カミルKiełczewski19年

@KamilKiełczewski:どうもありがとう。数字を16進数に効率的に変換することは、私のお気に入りの1つです。これは、いくつかの巧妙なトリックとビット操作の素晴らしいユースケースです。
ピーター

3

Scala58 40 36バイト

"%08X"format Integer.reverseBytes(_)

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

依然としてのバイトを逆にする組み込みを使用Intするが、用途はformatフォーマットするInt六角として。電話する必要はありませんtoHexString

のかっこを削除しましたformat。これは、引数を暗黙的に使用できることを意味し_ます。


2

Forth(gforth)52 51 40バイト

: f hex 0 4. do <# # # 0. #> type loop ;

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

コードの説明

: f           \ start a new word definition
  hex         \ set the current base to base 16
  0           \ convert the input number to a double-cell integer
  4. do       \ start a counted loop from 0 to 3
    <# # #    \ start a formatted numeric string and move last 2 digits to format area
    0.        \ move remaining digits down the stack
    #>        \ delete top two stack value and convert format area to string
    type      \ output string
  loop        \ end loop
;             \ end word definition




2

K412の 11バイト

解決:

,/$|4_0x0\:

例:

q)k),/$|4_0x0\:304767
"7fa60400"
q)0W
"0004a67f"

説明:

質問がほぼ正確に尋ねるもの:

,/$|4_0x0\: / the solution
      0x0\: / split to bytes
    4_      / drop first 4 bytes
   |        / reverse
  $         / convert to string
,/          / flatten

ノート:

  • K4番号はデフォルトで長い(64ビット)ので、-1バイトなので、4バイト(32ビット)をドロップします

🚀あなたの答えはトップにあります
カミルKiełczewski19年

2

PHP、31バイト

<?=unpack(H8,pack(V,$argn))[1];

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

PHPのpackunpackを利用して、「32ビットリトルエンディアンバイトオーダー」形式(V)の符号なし入力をバイナリ文字列にパックしてから、「hex string、high nibble first」形式(H)で、結果を出力します。

これは、PHPのビルトインが実際に単純なアルゴリズムを実装するよりも短いまれなケースの1つです。


PHPのpack()/ unpack()関数は、ほとんどのPHPプロジェクトでこれまでに必要なことはありませんでした。おめでとうございます、あなたはそれらの使用を見つけました!
640KB

1

、11バイト

⪫⮌⪪﹪%08xN²ω

オンラインでお試しください!リンクは、コードの詳細バージョンです。説明:

        N   Input as a number
   ﹪%08x    Format using literal string
  ⪪      ²  Split into pairs of characters
 ⮌          Reverse
⪫         ω Join
            Implicitly print

Pythonフォーマットに頼らずに19バイト:

⪫…⮌⪪⍘⁺X²¦³⁶N¹⁶¦²¦⁴ω

オンラインでお試しください!リンクは、コードの詳細バージョンです。説明:

           N        Input as a number
     ⁺              Plus
       ²            Literal 2
      X             To power
         ³⁶         Literal 36
    ⍘               Convert to base
            ¹⁶      Literal 16
   ⪪           ²    Split into pairs of digits
  ⮌                 Reverse the list
 …               ⁴  Take the first 4 pairs
⪫                 ω Join together
                    Implicitly print

🚀あなたの答えはトップにあります
カミルKiełczewski19年


1

J、10バイト

8{._1{3!:3

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

どうやって

3!:3ここに記載されている16進表現のJ「外部結合」です。つまり、16進数に変換するためのビルトインです。しかし、それは私たちが望んでいるものとはまったく異なる出力です。たとえば、実行中:

3!:3 (304767)

生成するもの:

e300000000000000
0400000000000000
0100000000000000
0000000000000000
7fa6040000000000

他の行の意味は、上記にリンクしたドキュメントページで説明されています。いずれにしても、最後の行の最初の8文字が必要であることは明らかです。

_1{ 最後の行を取得します。

8{. 最初の8文字を取得します。


🚀あなたの答えはトップにあります
カミルKiełczewski19年


1

Windowsバッチ、90バイト

@for /l %%x in (24,-8,0)do @set/aa=%1^>^>%%x^&255&cmd/cexit !a!&<nul set/p=!=exitcode:~-2!

/ vを指定してコマンドラインを実行し、遅延展開を有効にします。


1

x86 32ビットマシンコード、24 21バイト

変更ログ:-3バイト:標準のadd / cmp / jbe / addを@peter ferrieによるDASハックに置き換えます

64ビット:まだ24バイト。ロングモードはDASオペコードを削除しました。
16ビットモード:デフォルトのオペランドサイズは16ビットですが、問題の仕様は本質的に32ビットです。ハードコードされた8桁の16進数を含む。


bswap標準の順序で手動でint-> hexを使用してバイトを反転します(最上位ニブルを最初に、16進数をchar出力バッファーに昇順で書き込みます)。これにより、バイト内のニブル間の順序を切り替えるループを展開する必要がなくなりますバイト間。

void lehex(char buf[8] /*edi*/, uint32_t x /*esi*/);x86-64 System Vのように呼び出し可能ですが、これは64ビットモードでは機能しません。(EDIの出力ポインタが必要ですstosb。入力番号は、ECXまたはEAX以外のレジスタに入れることができます。)

     1                             lehex:
     2 00000000 0FCE                   bswap  esi
     3 00000002 6A08                   push   8            ; 8 hex digits
     4 00000004 59                     pop    ecx
     5                             .loop:                ;do{
     6 00000005 C1C604                 rol    esi, 4       ; rotate high nibble to the bottom
     7                             
     8 00000008 89F0                   mov    eax, esi
     9 0000000A 240F                   and    al, 0x0f     ; isolate low nibble
    10 0000000C 3C0A                   cmp al, 10          ; set CF according to digit <= 9
    11 0000000E 1C69                   sbb al, 0x69        ; read CF, set CF and conditionally set AF
    12 00000010 2F                     das                 ; magic, which happens to work
    13                             
    14 00000011 AA                     stosb               ; *edi++ = al
    15 00000012 E2F1                   loop  .loop       ; }while(--ecx)
    16                             
    17 00000014 C3                     ret

サイズ= 0x15 = 21バイト。

TIO FASM 32ビットx86テストケースで、writeシステムコールを使用して出力を2回呼び出してから2つの文字列をバッファーに追加した後、システムコールを使用して出力を書き込みます。数字と文字の境界で9とAを含むすべての16進数0..Fをテストします。

DASハック - x86の低ニブルのうちキャリーのために、ハーフキャリーフラグを持っています。2桁の2桁のBCD整数を減算した後の使用を目的とした、DAS命令などのパックドBCDに役立ちます。ALのニブルが0〜9の範囲外であるため、ここで間違いなく悪用しています。

マニュアルの操作セクションのif (old_AL > 99H) or (old_CF = 1)THEN AL ← AL − 60H;部分に注意してください。sbbは常に CFをここに設定するため、一部が常に発生します。それと大文字のASCII範囲は、選択の動機となるものですsub al, 0x69

  • cmp 0xD, 0xA CFを設定しません
  • sbb 0xD - 0x69はAL =にラップします0xA4、DASへの入力としてます。(CFを設定し、AFをクリアします)
  • DASの最初の部分にAL-= 6はありません(4> 9が偽でAF = 0であるため)
  • AL-= 0x60の2番目の部分では0x44'D'

対数字:

  • cmp 0x3, 0xA CFを設定します
  • sbb 3 - 0x69 - 1 = AL = 0x99およびCFとAFを設定
  • DASの最初の部分にAL-= 6がありません(9> 9は偽ですが、AFは設定されています)、0x93を残します
  • AL-= 2番目の部分の0x60。0x33のASCIIコードを残します'3'

0x6aSBBで減算すると、すべての数字が9以下のすべての数字にAFが設定されるため、すべての数字は同じ論理に従います。そして、アルファベットの16進数字ごとにクリアしたままにします。すなわち、DASの9 / A分割処理を正しく活用します。


通常(パフォーマンスのため)、スカラーループのルックアップテーブルを使用するか、場合によってはブランチレス2x leaおよびcmp/cmov条件付き追加を使用します。ただし、2バイトのal, imm8命令は、コードサイズにとって大きなメリットです。


x86-64バージョンversion:との間and al, 0xfで異なる部分のみstosb

;; x86-64 int -> hex  in 8 bytes
    10 0000000C 0430                   add    al, '0'
    11 0000000E 3C39                   cmp    al, '9'
    12 00000010 7602                   jbe  .digit
    13 00000012 0427                     add    al, 'a'-10 - '0'     ; al =  al>9 ? al+'a'-10 : al+'0'
    14                             .digit:

通知はことをadd al, '0' 常に実行され、条件付きaddは唯一の違いを追加'a'-10して'0'、それだけで作ることifの代わりにif/else

テストおよび作品、それを用いmainて、発信者を私のCの答えは使用しています、char buf[8]printf("%.8s\n", buf)


ここでオンライン作業スニペットを作成できますか?
カミルKiełczewski19年

@KamilKiełczewski:TIOにより、呼び出し元をCで記述してasm関数をテストすることは不可能(不明)なので、気にすることはありませんが、sys_write固定長の文字列を簡単に要求して出力できるので、確かです。おもしろいことに、私はTIOでFASMが32ビットの実行可能ファイルを作成できることに気付いていませんでした-felf32。とにかくx86-64が好きで、この答えは32ビットコードのバイトを保存しません。
ピーター

⭐-あなたは素敵な答えのために星を取得します
カミルKiełczewski19年

1
@ JL2210:という意味sprintfですか?libcには、format-stringベースのもの以外の便利なint-> string関数はないと思います。strtoulのようなstring-> intのみです。ただし、ダイナミックライブラリの関数のGOTエントリのバイト数を数える方法を見つけ出せば(6バイトのcall [rel printf wrt ..got]呼び出しサイト以外に)、bswap / printfはおそらく短くなります。少なくともld通常のデフォルトで作成された場合、静的にリンクされた最小の実行可能ファイルは、動的よりも大幅に小さくなります。しかし、静的にリンクすることは合理的ではないと思いますが、コードサイズはカウントしません。
ピーター

1
@ JL2210:これはx86 マシンコードの回答であり、asmテキストソースのサイズではないことに注意してください。以前のマシンコードの回答ではlibc関数を使用せず、Linuxシステムコール(フィボナッチなど)、およびコストの計算方法やlibcのマシンコード回答を作成するかどうかをIDKのみで使用しました。 。libcが使用できないx86マシンコードのユースケースがあります(ブートローダーなど)。
ピーター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.