Wprowadzenie
Podczas pracy z generatorem BMP (bitmapy) napotykam problem z konwersją liczb na mały łańcuch szesnastkowy Endian. Oto funkcja, którą tworzę w JavaScript - ale zastanawiam się, jak mały kod może działać podobnie
let liEnd= num => num.toString(16).padStart(8,'0').match(/../g).reverse().join``;
console.log(liEnd(304767)) // 304767 dec = 0x4a67f hex
Wyzwanie
Funkcja zapisu, która pobierze 32-bitową liczbę całkowitą bez znaku na wejściu i wygeneruje 8-cyfrowy ciąg szesnastkowy o małej kolejności endianów. Przykładowy algorytm, który wykonuje zadanie:
- zamień numb na ciąg szesnastkowy np .:
304767 -> '4a67f'
- dodaj zera dopełniające, aby uzyskać ciąg 8 znaków:
'0004a67f'
- podzielony ciąg na cztery 2-znakowe kawałki:
'00','04','a6','7f'
- odwrotna kolejność sztuk
'7f','a6','04','00'
- połącz elementy i zwróć w wyniku:
'7fa60400'
Przykład wejścia i wyjścia
Numer wejściowy (lub ciąg z numerem dec) znajduje się po lewej stronie ->
, a wyjściowy ciąg szesnastkowy po prawej stronie
2141586432 -> 0004a67f
304767 -> 7fa60400
f=lambda n,i=4:i*'1'and'%02x'%(n%256)+f(n>>8,i-1)
zapisuje bajt :)R ,
5453 bajtówWypróbuj online!
Każda grupa 2 znaków jest w rzeczywistości szesnastkową reprezentacją cyfry w podstawie 256.
scan()%/%256^(0:3)%%256
konwertuje na podstawową liczbę 256 z odwróconymi 4 cyframi,...%*%256^(3:0)
łączy je jako jedną liczbę całkowitą iformat.hexmode(...,8)
konwertuje tę liczbę na reprezentację szesnastkową z 8 cyframi.źródło
JavaScript (ES7),
5957 bajtówManipulacja ciągiem.
Wypróbuj online!
W jaki sposób?
Najpierw konwertujemy na szesnastkowy, aby upewnić się, że wszystkie wiodące są uwzględnione:n + 232 0
Wypróbuj online!
Używamy wyrażenia regularnego,1
/\B../g
aby dopasować wszystkie grupy 2 cyfr, ignorując wiodącą dzięki ( granicy niebędącej słowem ).\B
Wypróbuj online!
My
reverse()
ijoin()
aby uzyskać ostatni ciąg.JavaScript (ES6), 61 bajtów
Funkcja rekurencyjna.
Wypróbuj online!
źródło
Zsh , 46 bajtów
Wypróbuj online!
źródło
C # (interaktywny kompilator Visual C #) , 54 bajty
Zaoszczędź 4 bajty dzięki @PeterCordes
Wypróbuj online!
Wyjaśnienie
źródło
4278255360
stałą maskę do16711935
(0xff00ff
), jeśli przesuniesz się przed maskowaniem? Czy to kosztuje dodatkowe pareny? Ponadto, jeśli nie, to0xff00ff00
jest tej samej długości, ale o wiele bardziej znaczący dla ludzi.>>
ma wyższy priorytet niż&
, dzięki czemu zaoszczędzono łącznie 4 bajty. Dzięki!Japt
-P
, 10 bajtówSpróbuj
źródło
-P
zrobić?-P
: Jeśli dane wyjściowe są tablicą, dane wyjściowe bez separatora (tzn. Połączone zP
). ”. Tak więc flaga służy do niejawnego zamiast jawnego łączenia w celu zapisania bajtów. :)C (gcc) , 30 bajtów
Wypróbuj online!
źródło
Python 2 , 43 bajty
Wypróbuj online!
-4 bajty dzięki benrg
Wyświetla listę znaków. Obliczany przez wyszukiwanie w kolejności cyfr szesnastkowych danych wejściowych przy indeksach
6, 7, 4, 5, 2, 3, 0, 1
.źródło
[i^6]for i in range(8)
oszczędza kilka bajtów.C (gcc) endian agnostic, bez standardowych bibliotek lib,
9291 bajtówh(n)
to jednocyfrowa funkcja pomocnicza liczby szesnastkowej.f(x,p)
przyjmuje liczbę całkowitą ichar[8]
wskaźnik. Wynik to 8 bajtówchar
danych. ( Nie kończy się na 0, chyba że dzwoniący to zrobi.)Założenia: zestaw znaków ASCII. Uzupełnienie 2,
int
więc prawe przesunięcie w końcu obniża bit znaku, a konwersjauint32_t
naint
nie nie przerywa wzoru bitowego, jeśli ustawiony jest wysoki bit.int
jest co najmniej 32-bitowy. (Szerszy może pozwolić, aby działał na uzupełnieniach 1 lub implementacjach C o sile znaku).Brak założeń: wszystko o bajtowej kolejności realizacji lub podpisaniu
char
.Wypróbuj online! w tym testujący dzwoniącego używający
printf("%.8s\n", buf)
do wydrukowania bufora wyjściowego bez zerowania go.Nie golfowany:
Robienie w
n&=15;
środkuh(x)
jest progiem rentowności; 6 bajtów tam w porównaniu do 3 dla&15
izolowania niskiego skubania w obu witrynach wywoławczych.,
jest punktem sekwencyjnym (lub równoważnym we współczesnej terminologii), więc można bezpiecznie zrobić*p++= stuff
dwa razy w jednym wyrażeniu, gdy zostanie rozdzielone przez,
operatora.>>
na liczbach całkowitych ze znakiem jest implementowana jako arytmetyczna lub logiczna. GNU C definiuje to jako uzupełnienie arytmetyki 2. Ale na maszynie dopełniającej 2 nie ma to tak naprawdę znaczenia, ponieważ nigdy nie patrzymy na przesunięte 0 lub kopie bitu znaku. Oryginalny MSB ostatecznie przejdzie do niskiego bajtu bez zmian. Nie dotyczy to znaku / wielkości i nie jestem pewien co do uzupełnienia 1.Może to być więc przenośne tylko dla implementacji C uzupełnienia 2. (Lub gdy
int
jest szersza niż 32 bity więc bit 31 jest tylko częścią tej wielkości.) Unsigned -> podpisana konwersji również munges bit-wzorzec dla ujemnych liczb całkowitych, więc&15
na zasadzieint
byłoby wyodrębnić tylko przekąski pierwotnej wartości bez znaku na 2 za uzupełnienie. Ponownie, chyba żeint
był szerszy niż 32-bitowy, więc wszystkie wejścia są nieujemne.Wersja golfowa ma UB od upadku z końca funkcji nieważności. Nie zwracać wartości, tylko po to, aby uniknąć deklarowania jej
void
zamiast wartości domyślnejint
. Nowoczesne kompilatory zepsują to przy włączonej optymalizacji.Motywacja: Zastanawiałem się nad odpowiedzią ASM x86 lub ARM Thumb, pomyślałem, że fajnie byłoby to zrobić ręcznie w C, być może dla asm wygenerowanego przez kompilator jako punkt wyjścia. Zobacz /programming/53823756/how-to-convert-a-number-to-hex, aby uzyskać energooszczędny system x86 asm, w tym wersję AVX512VBMI, która zawiera tylko 2 instrukcje (ale potrzebuje wektorów kontrolnych dla vpmultishiftqb i vpshufb więc nie byłoby świetnie do golfa). Zwykle SIMD wymaga dodatkowej pracy, aby odwrócić bajt do kolejności drukowania na little-endian x86, więc to wyjście w postaci odwróconego bajtu jest w rzeczywistości łatwiejsze niż normalnie.
Inne pomysły
Zastanawiałem się nad pobraniem liczby całkowitej przez odwołanie i zapętlenie jej bajtów
char*
na implementacji C-endian (takiej jak x86 lub ARM). Ale nie sądzę, by to wiele zaoszczędziło.Używanie
sprintf
do zrobienia 1 bajtu naraz, 64 bajty po grze w golfa:Ale jeśli korzystamy z funkcji podobnych do printf, równie dobrze moglibyśmy zamieniać bajty i robić
%x
printf całej rzeczy, takiej jak odpowiedź @ JL2210 .źródło
Kod maszynowy SIM86 x86 (AVX512-VBMI), 36 bajtów
(16 bajtów, które są tabelą wyszukiwania szesnastkowego)
Jest to funkcja, która przyjmuje liczbę całkowitą
xmm0
i zwraca 8 bajtów danych znakowych ASCIIxmm0
, aby osoba dzwoniąca zapisywała gdziekolwiek chce. (np. do pamięci wideo po przeplataniu bajtami atrybutów, w ciąg znaków w budowie lub cokolwiek innego)Od C, nazwij to jak
__m128i retval = lehex(_mm_cvtsi32_si128(x))
w konwencji wywoływania Systemu V x86-64 lub MS Windowsvectorcall
.Razem = 0x24 = 36 bajtów.
Zobacz Jak przekonwertować liczbę na heksadecymalną? na SO, jak to działa. (SSE2 dla shift / punpck, a następnie
vpermb
oszczędza pracę, której potrzebowalibyśmypshufb
. AVX1 zamiast SSE2 / SSSE3 również unikamovaps
kopiowania rejestru.)Zauważ, że
punpcklbw
przy operandach źródłowych w tej kolejności otrzymamy najbardziej znaczący skrawek niskiego bajtu wejściowego w elemencie najniższego bajtu, a następnie najmniej znaczący skrawek najniższego bajtu źródłowego. (W tej odpowiedzi SO,bswap
na wejściu użyto znaku a, aby uzyskać wynik w standardowej kolejności drukowania tylko z SSE2. Ale tutaj potrzebujemy tej kolejności: wysokiego skubania w dolnym elemencie w każdym bajcie, ale wciąż małej kolejności bajtów endian).Gdybyśmy mieli więcej stałych danych, moglibyśmy zaoszczędzić miejsce w trybie adresowania, wykonując jeden z nich,
mov edx, imm32
a następnie używając innych[rdx+16]
trybów adresowania. Lubvpbroadcastb xmm0, [rdx+1]
.Ale myślę, że 16-bajtowy hex LUT +
vpermb
jest wciąż lepszy niż implementacjan>9 : n+'a'-10 : n+'0'
warunku: wymaga 3 stałych i co najmniej 3 instrukcji z maskowaniem bajtów AVX512BW (porównaj do maski,vpaddb
maskowaniavpaddb
scalonego) lub więcej z AVX1 lub SSE2. (Zobacz Jak przekonwertować liczbę na heksadecymalną na SO dla tej wersji SSE2). Każda instrukcja AVX512BW ma długość co najmniej 6 bajtów (4-bajtowy EVEX + kod operacyjny + modrm), dłużej z przesunięciem w trybie adresowania.Właściwie zajęłoby to co najmniej 4 instrukcje, ponieważ przed porównaniem musimy wyczyścić wysokie śmieci za pomocą
andps
((lub EVEXvpandd
z 4-bajtowym operandem pamięci rozgłoszeniowej). I każda z nich potrzebuje innej stałej wektorowej. AVX512 ma operandy pamięci rozgłoszeniowej, ale tylko dla elementów 32-bitowych i szerszych. np. ostatni operand EVEXvpaddb
jest tylkoxmm3/m128
, a niexmm3/m128/m8bcst
. (Porty ładowania Intela mogą wykonywać 32-bitowe i 64-bitowe transmisje za darmo jako część pakietu ładowania, dlatego Intel zaprojektował AVX512BW, aby to odzwierciedlić i nie był w stanie zakodować operandów pamięci bajtów lub słów, zamiast dać im opcję wykonaj transmisje dword, abyś mógł nadal kompresować swoje stałe do 4 bajtów: /.)Powód, dla którego użyłem AVX512VBMI
vpermb
zamiast SSSE3 / AVX1pshufb
jest dwojaki:vpermb
ignoruje wysokie bity selektorów.(v)pshufb
zera bajtów zgodnie z wysokim bitem wektora kontrolnego i potrzebowałby dodatkowegopand
lubandps
faktycznie izolowałby skubki. Przy rozmiarze XMM / 16 bajtów,vpermb
patrzy tylko na 4 małe bity elementów sterujących tasowaniem, tj. Bity[3:0]
w notacji Intela w sekcji Operacja .vpermb
może potrwać dane do przetasowania (tabela odnośników) jako operand pamięci.(v)pshufb
Operand xmm / mem jest wektorem kontrolującym losowanie.Pamiętaj, że AVX512VBMI jest dostępny tylko na CannonLake / Ice Lake, więc prawdopodobnie potrzebujesz symulatora, aby to przetestować, na przykład SDE Intela.
źródło
Scala ,
584036 bajtówWypróbuj online!
Nadal używa wbudowanego do odwracania bajtów
Int
, ale używaformat
do formatowaniaInt
jako Hex. Nie musisz dzwonićtoHexString
.Usunięto pareny na
format
. Oznacza to teraz, że argument można przyjąć domyślnie za pomocą_
.źródło
Dalej (gforth) ,
52 5140 bajtówWypróbuj online!
Wyjaśnienie kodu
źródło
Galaretka , 13 bajtów
Wypróbuj online!
Pełny program, który jako argument przyjmuje liczbę całkowitą i wypisuje ciąg.
źródło
APL + WIN,
3634 bajtów2 bajty zapisane przez konwersję do zera indeksu
Monity o liczbę całkowitą:
Wypróbuj online! Dzięki uprzejmości Dyalog Classic
źródło
Excel, 91 bajtów
źródło
K4 ,
1211 bajtówRozwiązanie:
Przykłady:
Wyjaśnienie:
Prawie dokładnie to, o co pyta pytanie:
Uwagi:
źródło
PHP , 31 bajtów
Wypróbuj online!
Korzystając z pakietu PHP i rozpakuj , pakuję niepodpisane dane wejściowe w formacie „32 bit little endian byte order” (
V
) w ciąg binarny, a następnie rozpakowuję go w formacie „hex hex, najpierw high nibble najpierw” (H
) i wypisuję wynik.Wydaje się, że jest to jeden z rzadkich przypadków, w których wbudowane PHP są w rzeczywistości krótsze niż implementacja prostego algorytmu!
źródło
pack()
/unpack()
funkcje są niesamowite, ponieważ 0 razy potrzebujesz ich w większości projektów PHP. Gratulacje, znalazłeś ich zastosowanie!Węgiel drzewny , 11 bajtów
Wypróbuj online! Link jest do pełnej wersji kodu. Wyjaśnienie:
19 bajtów bez uciekania się do formatowania w języku Python:
Wypróbuj online! Link jest do pełnej wersji kodu. Wyjaśnienie:
źródło
Perl 5 (-p), 22 bajty
Wypróbuj online!
źródło
J , 10 bajtów
Wypróbuj online!
w jaki sposób
3!:3
jest udokumentowaną tutaj „zagraniczną koniunkcją” dla reprezentacji szesnastkowej . Oznacza to, że jest wbudowanym narzędziem do konwersji na hex. Jednak nie jest to dokładnie to, czego chcemy. Np. Bieganie:produkuje:
Znaczenie innych wierszy wyjaśniono na stronie z dokumentami, do której prowadziłem powyżej. W każdym razie jest jasne, że chcemy pierwszych 8 znaków w ostatniej linii.
_1{
dostać ostatnią linię.8{.
pobiera pierwsze 8 znaków.źródło
Rubin ,
3127 bajtówSkończyło się to odpowiedzią PHP na Night2, ponieważ Ruby ma tę samą funkcjonalność pakowania / rozpakowywania.
Wypróbuj online!
Moja oryginalna 31-bajtowa odpowiedź, która nie korzystała z trybu rozpakowywania H8, ponieważ nie wiedziałam o tym:
Wypróbuj online!
źródło
Pakiet Windows, 90 bajtów
Uruchom wiersz poleceń za pomocą / v, aby włączyć opóźnione rozszerzenie.
źródło
x86 32-bitowy kod maszynowy,
2421 bajtówdziennik zmian: -3 bajty: zamień standardowy add / cmp / jbe / add na hack DAS przez @peter ferrie
64-bit: nadal 24 bajty. Tryb długi usunął kod operacyjny DAS.
Tryb 16-bitowy: domyślny rozmiar operandu to 16-bit, ale specyfikacja problemu jest z natury 32-bitowa. Łącznie z zakodowanymi 8 cyframi szesnastkowymi.
bswap
Odwracanie bajtów, a następnie ręczne int-> hex w standardowej kolejności (najpierw najbardziej znaczące skubanie, zapisywanie cyfr szesnastkowych w buforze wyjściowym char w porządku rosnącym). Pozwala to uniknąć konieczności rozwijania pętli w celu przełączania kolejności między skubkami w bajcie vs. przez bajty.Można wywoływać
void lehex(char buf[8] /*edi*/, uint32_t x /*esi*/);
jak w systemie x86-64 System V, ale nie działa to w trybie 64-bitowym. (Potrzebuje wskaźnika wyjściowego w EDI dlastosb
. Numer wejściowy może znajdować się w dowolnym rejestrze innym niż ECX lub EAX.)rozmiar = 0x15 = 21 bajtów.
32-bitowa walizka testowa TIO FASM x86 z wywołującym asm, który używa
write
wywołania systemowego do zapisu danych wyjściowych po dwukrotnym wywołaniu go w celu dołączenia 2 łańcuchów do bufora. Testuje wszystkie cyfry szesnastkowe 0..F, w tym 9 i A na granicy między cyfrą a literą.DAS
Hack - x86 ma flagę pół-carry, dla przeprowadzenia niskiej skubać. Przydatny do rzeczy z zapakowanym BCD, takich jak instrukcja DAS, przeznaczonych do użycia po odjęciu dwóch 2-cyfrowych liczb całkowitych BCD. Ponieważ niski poziom AL jest poza zakresem 0-9, zdecydowanie nadużywamy go tutaj.Zwróć uwagę
if (old_AL > 99H) or (old_CF = 1)
NA TO,AL ← AL − 60H;
część rozdziału Obsługa w podręczniku; sbb zawsze ustawia tutaj CF, więc ta część zawsze się dzieje. To i zakres ASCII dla wielkich liter motywuje do wyborusub al, 0x69
cmp 0xD, 0xA
nie ustawia CF0xD - 0x69
zawijany do AL =0xA4
jako dane wejściowe do DAS. (I ustawia CF, czyści AF)0x44
kod ASCII dla'D'
kontra cyfra:
cmp 0x3, 0xA
ustawia CF3 - 0x69 - 1
= AL = 0x99 i ustawia CF i AF'3'
.Odejmowanie
0x6a
w SBB spowoduje ustawienie AF dla każdej cyfry <= 9, aby wszystkie cyfry były zgodne z tą samą logiką. I pozostaw to wyczyszczone dla każdej alfabetycznej cyfry szesnastkowej. tj. prawidłowe wykorzystanie dzielonej obsługi 9 / A DAS.Zwykle (dla wydajności) użyłbyś tabeli przeglądowej dla pętli skalarnej lub ewentualnie bez rozgałęzienia 2x
lea
icmp/cmov
dodawania warunkowego. Aleal, imm8
instrukcje 2-bajtowe to duża wygrana dla rozmiaru kodu.Wersja w wersji x86-64 : tylko inna część, pomiędzy
and al, 0xf
istosb
.Zauważ, że
add al, '0'
zawsze działa, a dodawanie warunkowe dodaje tylko różnicę między'a'-10
i'0'
, aby było to po prostuif
zamiastif
/else
.Testowany i działa, używając tego samego
main
wywołującego co moja odpowiedź C , który używachar buf[8]
iprintf("%.8s\n", buf)
.źródło
sys_write
mogę łatwo wypisać ciągi o stałej długości. Och, ciekawe, nie zdałem sobie sprawy, że FASM na TIO pozwala tworzyć 32-bitowe pliki wykonywalne, w przeciwieństwie do NASM, w którym nie szanuje-felf32
. W każdym razie wolę x86-64, a ta odpowiedź nie zapisuje żadnych bajtów z kodu 32-bitowego.sprintf
? Nie sądzę, że libc ma jakieś przydatne funkcje int-> string inne niż oparte na format-string, tylko string-> int jak strtoul. Ale tak, bswap / printf byłby prawdopodobnie krótszy, gdybyś mógł znaleźć jakiś sposób na policzenie bajtów dla wpisu GOT dla funkcji w bibliotece dynamicznej (oprócz 6-bajtowejcall [rel printf wrt ..got]
witryny wywoływania); minimalne statycznie powiązane pliki wykonywalne mogą być znacznie mniejsze niż dynamiczne, przynajmniej jeśli są tworzoneld
przy normalnych ustawieniach domyślnych. Ale nie sądzę, że rozsądne byłoby statyczne łączenie go, ale nie liczenie jego rozmiaru kodu.