| Bytes | Lang | Time | Link |
|---|---|---|---|
| 014 | Pip | 251014T205932Z | DLosc |
| 053 | JavaScript Node.js | 251014T184440Z | l4m2 |
| 034 | Python 3.11+ | 230419T142544Z | ShadowRa |
| 021 | x86 32bit machine code | 191002T100035Z | Peter Co |
| 037 | Red | 210601T174713Z | 9214 |
| 034 | Rust | 210601T195400Z | Aiden4 |
| 090 | Windows Batch | 191004T193836Z | peter fe |
| 027 | Ruby | 191003T224149Z | Value In |
| 043 | Python 2 | 191002T131858Z | negative |
| 010 | J | 191001T231543Z | Jonah |
| 031 | PHP | 191003T130351Z | Night2 |
| 022 | Perl 5 p | 191003T131500Z | Grimmy |
| 011 | K4 | 191002T110124Z | mkst |
| 054 | C# Visual C# Interactive Compiler | 191001T202033Z | Gymhgy |
| 030 | C gcc | 191001T190304Z | S.S. Ann |
| 036 | Scala | 191002T092733Z | Soapy |
| 036 | x86 SIMD machine code AVX512VBMI | 191002T092724Z | Peter Co |
| 011 | Charcoal | 191002T094649Z | Neil |
| 091 | Excel | 191002T092820Z | Wernisch |
| 010 | Japt P | 191001T191545Z | Shaggy |
| 091 | C gcc endian agnostic | 191002T074200Z | Peter Co |
| 009 | 05AB1E | 191001T184018Z | Dorian |
| 046 | Zsh | 191001T232751Z | GammaFun |
| 053 | R | 191001T183823Z | Robin Ry |
| 034 | APL+WIN | 191001T202454Z | Graham |
| 013 | Jelly | 191001T203925Z | Nick Ken |
| 040 | Forth gforth | 191001T183700Z | reffu |
| 037 | Python 3 | 191001T190326Z | Joel |
| 057 | JavaScript ES7 | 191001T172427Z | Arnauld |
Pip, 14 bytes
Sa+E32TB16<>-2
Explanation
Sa+E32TB16<>-2
a Command-line argument
+E32 Add 2^32 for padding
TB16 Convert to base 16
S Remove the leading 1 from the 2^32 addition
<>-2 Break into groups of 2 from right to left
Concatenate and autoprint (implicit)
JavaScript (Node.js), 53 bytes
f=(n,i=8)=>i--?(n>>4*(i^6)&15).toString(16)+f(n,i):''
JavaScript (Node.js), 55 bytes
n=>'10325476'.replace(/./g,t=>(n>>4*t&15).toString(16))
Python 3.11+, 34 bytes
lambda n:n.to_bytes(4)[::-1].hex()
This is a version of Joel's Python 3 solution that works only on Python 3.11 and higher. Pre-3.11, the byteorder argument to int.to_bytes/int.from_bytes was mandatory; in 3.11 it is now defaulted to 'big', so, golf-wise, you can omit it, let to_bytes produce the big-endian version, then reverse it with a slice, saving three characters.
x86 32-bit machine code, 24 21 bytes
changelog: -3 bytes: replace standard add/cmp/jbe/add with a DAS hack by @peter ferrie
64-bit: still 24 bytes. Long mode removed the DAS opcode.
16-bit mode: the default operand-size is 16-bit but the problem spec is inherently 32-bit. Including hard-coded 8 hex digits.
Byte-reverse with bswap then manual int->hex in standard order (most-significant nibble first, writing hex digits to a char output buffer in ascending order.) This avoids needing to unroll the loop to switch order between nibbles within a byte vs. across bytes.
Callable as void lehex(char buf[8] /*edi*/, uint32_t x /*esi*/); like x86-64 System V, except this doesn't work in 64-bit mode. (It needs the output pointer in EDI for stosb. The input number can be in any register other than ECX or 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
size = 0x15 = 21 bytes.
TIO FASM 32-bit x86 test case with an asm caller that uses a write system call to write the output after calling it twice to append 2 strings into a buffer. Tests all hex digits 0..F, including 9 and A at the boundary between numeral vs. letter.
The DAS hack - x86 has a half-carry flag, for carry out of the low nibble. Useful for packed-BCD stuff like the DAS instruction, intended for use after subtracting two 2-digit BCD integers. With the low nibble of AL being outside the 0-9 range, we're definitely abusing it here.
Notice the if (old_AL > 99H) or (old_CF = 1) THEN AL ← AL − 60H; part of the Operation section in the manual; sbb always sets CF here so that part always happens. That and the ASCII range for upper-case letters is what motivates the choice of sbb al, 0x69.
cmp 0xD, 0xAdoesn't set CF- sbb
0xD - 0x69wraps to AL=0xA4as input to DAS. (And sets CF, clears AF) - no AL -= 6 in the first part of DAS (because 4 > 9 is false and AF=0)
- AL -= 0x60 in the second part, leaving
0x44, the ASCII code for'D'
vs. a numeral:
cmp 0x3, 0xAsets CF- sbb
3 - 0x69 - 1= AL = 0x99 and sets CF and AF - AL -= 6 in the first part of DAS (9 > 9 is false but AF is set), leaving 0x93
- AL -= 0x60 in the second part, leaving 0x33, the ASCII code for
'3'.
Subtracting 0x6a in SBB will set AF for every digit <= 9 so all the numerals follow the same logic. And leave it cleared for every alphabetic hex digit. i.e. correctly exploiting the 9 / A split handling of DAS.
'A' is 0x41, '9' is 0x39. So 0xa + '0' needs an extra 7 added to get from '9'+1 to 'A'. DAS involves a -=6 or -=0x60, hence the use of SBB to subtract an extra 1 for one of the cases.
Normally (for performance) you'd use a lookup table for a scalar loop, or possibly a branchless 2x lea and cmp/cmov conditional add. But 2-byte al, imm8 instructions are a big win for code-size.
x86-64 version version: just the part that's different, between and al, 0xf and 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:
Notice that the add al, '0' always runs, and the conditional add only adds the difference between 'a'-10 and '0', to make it just an if instead of if/else.
Tested and works, using the same main caller as my C answer, which uses char buf[8] and printf("%.8s\n", buf).
Red, 37 bytes
func[x][reverse/skip form to-hex x 2]
This breaks TIO because of the outdated compiler it uses.
Red, 43 bytes
func[x][load next mold reverse to-binary x]
Rust, 34 bytes
|n|print!("{:08x}",n.swap_bytes())
Rust has built-in hex formatting, but it displays in big-endian format, so I reversed the byte order first. Capitalize the x if you want uppercase hex digits instead of lowercase ones.
Windows Batch, 90 bytes
@for /l %%x in (24,-8,0)do @set/aa=%1^>^>%%x^&255&cmd/cexit !a!&<nul set/p=!=exitcode:~-2!
Run the command-line with /v to enable the delayed expansion.
Ruby, 31 27 bytes
Ended up being a port of Night2's PHP answer because Ruby has the same pack/unpack functionality.
->*i{i.pack(?V).unpack'H8'}
My original 31-byte answer that didn't take advantage of the H8 unpack mode because I didn't know about it:
->*i{'%02x'*4%i.pack(?V).bytes}
Python 2, 43 bytes
lambda n:[("%08x"%n)[i^6]for i in range(8)]
-4 bytes thanks to benrg
Outputs a list of characters. Computed by retrieving, in order, the hex digits of the input at indices 6, 7, 4, 5, 2, 3, 0, 1.
J, 10 bytes
8{._1{3!:3
how
3!:3 is a J "foreign conjunction" for hex representation, documented here. That is, it's a builtin for converting to hex. However, it's output it not quite what we want. Eg, running:
3!:3 (304767)
produces:
e300000000000000
0400000000000000
0100000000000000
0000000000000000
7fa6040000000000
The meaning of the other lines is explained on the doc page I linked to above. In any case, it's clear we want the first 8 chars of the last line.
_1{ get the last line.
8{. gets the first 8 characters of it.
PHP, 31 bytes
<?=unpack(H8,pack(V,$argn))[1];
Taking advantage of PHP's pack and unpack, I pack the unsigned input with "32 bit little endian byte order" format (V) into a binary string and then unpack it with "hex string, high nibble first" format (H) and print the result.
This seems to be one of the rare cases where PHP's built-ins are actually shorter than implementing a simple algorithm!
K4, 12 11 bytes
Solution:
,/$|4_0x0\:
Examples:
q)k),/$|4_0x0\:304767
"7fa60400"
q)0W
"0004a67f"
Explanation:
Pretty much exactly what the question asks:
,/$|4_0x0\: / the solution
0x0\: / split to bytes
4_ / drop first 4 bytes
| / reverse
$ / convert to string
,/ / flatten
Notes:
- -1 byte as K4 numbers are longs (64bit) by default, so dropping 4 bytes (32bits)
C# (Visual C# Interactive Compiler), 54 bytes
x=>$"{(x=x>>16|x<<16)>>8&16711935|(x&16711935)<<8:x8}"
Saved 4 bytes thanks to @PeterCordes
Explanation
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
Scala, 58 40 36 bytes
"%08X"format Integer.reverseBytes(_)
Still uses the builtin to reverse the bytes of an Int, but uses format to format the Int as a Hex. No need to call toHexString.
Removed the parens on format. This now means that the argument can be taken implicitly using _.
x86 SIMD machine code (AVX512-VBMI), 36 bytes
(16 bytes of which are a hex lookup table)
This is a function that takes an integer in xmm0 and returns 8 bytes of ASCII char data in xmm0, for the caller to store wherever it wants. (e.g. to video memory after interleaving with attribute bytes, or into a string under construction, or whatever)
From C, call it as __m128i retval = lehex(_mm_cvtsi32_si128(x)) with the x86-64 System V calling convention, or 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"
Total = 0x24 = 36 bytes.
See How to convert a number to hex? on SO for how this works. (SSE2 for the shift / punpck, then vpermb saves work that we'd need for pshufb. AVX1 instead of SSE2/SSSE3 also avoids a movaps register copy.)
Notice that punpcklbw with the source operands in that order will give us the most-significant nibble of the low input byte in the lowest byte element, then the least-significant nibble of the lowest source byte. (In that SO answer, a bswap is used on the input to get a result in standard printing order with only SSE2. But here we want that order: high nibble in lower element within each byte, but still little-endian byte order).
If we had more data constants, we could save addressing-mode space by doing one mov edx, imm32 then using [rdx+16] or whatever addressing modes. Or vpbroadcastb xmm0, [rdx+1].
But I think a 16-byte hex LUT + vpermb is still better than implementing the n>9 : n+'a'-10 : n+'0' condition: that requires 3 constants and at least 3 instructions with AVX512BW byte-masking (compare into mask, vpaddb, merge-masked vpaddb), or more with AVX1 or SSE2. (See How to convert a number to hex? on SO for an SSE2 version of that). And each AVX512BW instruction is at least 6 bytes long (4-byte EVEX + opcode + modrm), longer with a displacement in the addressing mode.
Actually it would take at least 4 instructions because we need to clear high garbage with andps, (or EVEX vpandd with a 4-byte broadcast memory operand) before the compare. And each of those needs a different vector constant. AVX512 has broadcast memory operands, but only for elements of 32-bit and wider. e.g. EVEX vpaddb's last operand is only xmm3/m128, not xmm3/m128/m8bcst. (Intel's load ports can only do 32 and 64-bit broadcasts for free as part of a load uop so Intel designed AVX512BW to reflect that and not be able to encode byte or word broadcast memory operands at all, instead of giving them the option to do dword broadcasts so you can still compress your constants to 4 bytes :/.)
The reason I used AVX512VBMI vpermb instead of SSSE3 / AVX1 pshufb is twofold:
vpermbignores high bits of the selectors.(v)pshufbzeros bytes according to the high bit of the control vector and would have needed an extrapandorandpsto actually isolate nibbles. With XMM / 16-byte size,vpermbonly looks at the low 4 bits of the shuffle-control elements, i.e. bits[3:0]in Intel's notation in the Operation section.vpermbcan take the data to be shuffled (the lookup table) as a memory operand.(v)pshufb's xmm/mem operand is the shuffle-control vector.
Note that AVX512VBMI is only available on CannonLake / Ice Lake so you probably need a simulator to test this, like Intel's SDE.
Charcoal, 11 bytes
⪫⮌⪪﹪%08xN²ω
Try it online! Link is to verbose version of code. Explanation:
N Input as a number
﹪%08x Format using literal string
⪪ ² Split into pairs of characters
⮌ Reverse
⪫ ω Join
Implicitly print
19 bytes without resorting to Python formatting:
⪫…⮌⪪⍘⁺X²¦³⁶N¹⁶¦²¦⁴ω
Try it online! Link is to verbose version of code. Explanation:
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
Excel, 91 bytes
=RIGHT(DEC2HEX(A1,8),2)&MID(DEC2HEX(A1,8),5,2)&MID(DEC2HEX(A1,8),3,2)&LEFT(DEC2HEX(A1,8),2)
Japt -P, 10 bytes
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
C (gcc) endian agnostic, no standard libs, 92 91 bytes
h(n) is a single-digit integer->hex helper function.
f(x,p) takes an integer and a char[8] pointer. The result is 8 bytes of char data. (Not 0-terminated unless the caller does that.)
Assumptions: ASCII character set. 2's complement int so right shift eventually brings down the sign bit, and converting a uint32_t to int doesn't munge the bit-pattern if the high bit is set. int is at least 32-bit. (Wider might let it work on 1's complement or sign-magnitude C implementations).
Non-assumptions: anything about implementation byte-order or signedness of 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);}
Try it online! including test caller using printf("%.8s\n", buf) to print output buffer without 0-terminating it.
Ungolfed:
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);
}
Doing n&=15; inside h(x) is break-even; 6 bytes there vs. 3 each for &15 to isolate the low nibble at both call sites.
, is a sequence point (or equivalent in modern terminology) so it's safe to do *p++= stuff twice in one statement when separated by the , operator.
>> on signed integer is implementation-defined as either arithmetic or logical. GNU C defines it as arithmetic 2's complement. But on any 2's complement machine it doesn't really matter because we never look at the shifted-in 0s or copies of the sign bit. The original MSB will eventually get down into the low byte unchanged. This is not the case on sign/magnitude, and I'm not sure about 1's complement.
So this may only be portable to 2's complement C implementations. (Or where int is wider than 32 bits so bit 31 is just part of the magnitude.) unsigned -> signed conversion also munges the bit-pattern for negative integers, so &15 on an int would only extract nibbles of the original unsigned value on 2's complement. Again, unless int was wider than 32-bit so all inputs are non-negative.
The golfed version has UB from falling off the end of a non-void function. Not to return a value, just to avoid declaring it void instead of default int. Modern compilers will break this with optimization enabled.
Motivation: I was considering an x86 or ARM Thumb asm answer, thought it might be fun to do it manually in C, maybe for compiler-generated asm as a starting point. See https://stackoverflow.com/questions/53823756/how-to-convert-a-number-to-hex for speed-efficient x86 asm, including an AVX512VBMI version that's only 2 instructions (but needs control vectors for vpmultishiftqb and vpshufb so wouldn't be great for golf). Normally it takes extra work for SIMD to byte-reverse into printing order on little-endian x86 so this byte-reversed hex output is actually easier than normal.
Other ideas
I considered taking the integer by reference and looping over its bytes with char*, on a little-endian C implementation (like x86 or ARM). But I don't think that would have saved much.
Using sprintf to do 1 byte at a time, 64 bytes after golfing:
int i;
void f(x,p)char*p;{
for(i=4;sprintf(p,"%.2x",x&255),--i;x>>=8)
p+=2;
}
But if we're using printf-like functions we might as well byte-swap and do a %x printf of the whole thing like @JL2210's answer.
05AB1E, 10 9 bytes
žJ+h¦2ôRJ
-1 byte by inspiration of the Jelly answer.
žJ+ add 2^32 to input
h convert to hex
¦ drop leading 1
2ô split in groups of 2
R reverse groups
J and join them
R, 54 53 bytes
format.hexmode(scan()%/%256^(0:3)%%256%*%256^(3:0),8)
Each group of 2 characters is actually the hex representation of a digit in base 256. scan()%/%256^(0:3)%%256 converts to a base 256 number with 4 digits reversed,
...%*%256^(3:0) joins them as a single integer, and format.hexmode(...,8) converts that number to its hex representation with 8 digits.
APL+WIN, 36 34 bytes
2 bytes saved by converting to index zero
Prompts for integer:
'0123456789abcdef'[,⊖4 2⍴(8⍴16)⊤⎕]
Jelly, 13 bytes
+Ø%b⁴Ḋs2Ṛ‘ịØh
A full program that takes an integer as its argument and prints a string.
Forth (gforth), 52 51 40 bytes
: f hex 0 4. do <# # # 0. #> type loop ;
Code explanation
: 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
Python 3, 37 bytes
lambda n:n.to_bytes(4,"little").hex()
Arithmetic-based recursive solution (50 49 bytes, works also for Python 2):
f=lambda n,i=4:i*'1'and"%02x"%(n%256)+f(n>>8,i-1)
-1 byte thanks to @JonathanAllan
JavaScript (ES7), 59 57 bytes
String manipulation.
n=>(n+2**32).toString(16).match(/\B../g).reverse().join``
How?
We first convert \$n + 2^{32}\$ to hexadecimal to make sure that all leading \$0\$'s are included:
(304767 + 2**32).toString(16) // --> '10004a67f'
We use the regular expression /\B../g to match all groups of 2 digits, ignoring the leading \$1\$ thanks to \B (non-word boundary).
'10004a67f'.match(/\B../g) // --> [ '00', '04', 'a6', '7f' ]
We reverse() and join() to get the final string.
JavaScript (ES6), 61 bytes
Recursive function.
f=(n,k=4)=>k?[(x=n&255)>>4&&'']+x.toString(16)+f(n>>8,k-1):''