C (gcc) endian agnostic, bez standardowych bibliotek lib, 92 91 bajtów
h(n)to jednocyfrowa funkcja pomocnicza liczby szesnastkowej.
f(x,p)przyjmuje liczbę całkowitą i char[8]wskaźnik. Wynik to 8 bajtów chardanych. ( Nie kończy się na 0, chyba że dzwoniący to zrobi.)
Założenia: zestaw znaków ASCII. Uzupełnienie 2, intwięc prawe przesunięcie w końcu obniża bit znaku, a konwersja uint32_tna intnie nie przerywa wzoru bitowego, jeśli ustawiony jest wysoki bit. intjest 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.
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);}
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:
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);
}
Robienie w n&=15;środku h(x)jest progiem rentowności; 6 bajtów tam w porównaniu do 3 dla &15izolowania 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++= stuffdwa 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 intjest 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 &15na zasadzie intbyłoby wyodrębnić tylko przekąski pierwotnej wartości bez znaku na 2 za uzupełnienie. Ponownie, chyba że intbył 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 voidzamiast wartości domyślnej int. 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 sprintfdo zrobienia 1 bajtu naraz, 64 bajty po grze w golfa:
int i;
void f(x,p)char*p;{
for(i=4;sprintf(p,"%.2x",x&255),--i;x>>=8)
p+=2;
}
Ale jeśli korzystamy z funkcji podobnych do printf, równie dobrze moglibyśmy zamieniać bajty i robić %xprintf całej rzeczy, takiej jak odpowiedź @ JL2210 .