x86 machine code, 7 bytes
Function that works in 32 or 64-bit mode, with inputs:
- length
n
of the bitstring, in ECX (really just CL)
- bit-pattern in the low
n
bits of EAX. (bits in "a number" is an allowed input format)
Output: Signed integer in the full width of EAX.
In binary 2's complement format, as standard for x86.
Apparently a "number" is a valid output, with no need to convert it ourselves to something having anything to do with decimal, judging by other existing answers, except for a C answer that actually calls printf
after sign-extending to fill an int
. (And languages that implicitly print numeric output as a decimal string, but most of the non-golf-language answers are functions that return a number.)
NASM listing: address, machine-code bytes, source
sign_extend_nbits:
00 F7D9 neg ecx ; 32-n = -n after masking to &31
02 D3E0 shl eax, cl ; x86 shifts implicitly mask the count &31
04 D3F8 sar eax, cl
06 C3 ret
Try it online!
Algorithm: Left shift so the MSB of the narrow input is at the top of the full register (count = 32-n), then arithmetic right-shift back to where it was, leaving the upper bits filled with copies of the MSB. x86 is a 2's complement machine; its arithmetic right shift does 2's complement sign extension.
This problem reduces to 2's complement sign-extension, with no need to read an ASCII string of base 2 digits, as stated by the question's input formats. (But this output format intentionally violates the stated "decimal" output requirement of the question the same way many other answers do. We could div
in a loop, or maybe fild
/ fbstp
to make BCD in about twice as many bytes.)
x86 shifts mask their count with &31
(for 8, 16, and 32-bit operand-size), so the -n
= CHAR_BIT*sizeof(int)-n
trick only works for 32 or 64-bit shifts, not 8 or 16. 8086 didn't mask shift counts at all, and I think when they introduced masking for performance reasons in 186 and 286, they wanted to still allow shifting out all the bits of a 16-bit register. But when 32-bit regs were new, masking was already a thing so they could choose whatever semantics they wanted.
This "protest" answer is intended to show how trivial the problem is with the stated allowed input formats, if we follow the precedent of other answers ignoring the "decimal" output requirement.
Computers, and most computer languages, use binary integers. In most of those languages, operators like bitwise n & 1
being the same as % 2
(for positive numbers), and x<<1
being the same as x*2
, prove that numbers are binary. Converting an integer to an array or ASCII string of decimal digits takes extra work, as in https://stackoverflow.com/questions/13166064/how-do-i-print-an-integer-in-assembly-level-programming-without-printf-from-the/46301894#46301894 (Or perhaps use x86 fild
/ fbstp
to store the float as packed BCD. Yes this instruction exists, and yes it's microcoded and very slow)
If you don't want to require that, don't say "decimal", just say "integer".
But don't maybe don't allow input formats that are already 2's complement integers? A C answer of f(x){return x;}
is arguably valid if we say that the 2's complement input number must already be in an int
, which is a fixed-width type and thus requires 2's complement sign-extension for valid integers. I decided to support variable widths by taking a value + length instead of just writing a 1-byte C3 ret
as a full protest answer, to stick to the spirit of what seemed to be the challenge, 2's complement sign extension without any actual conversion necessary.
int
rather than an array of decimal digits? If so, along with the input being allowed as a number, doesn't that mean we can doint f(x){return x;}
becausex
is already a 2's complementint
when compiled for a 2's complement machine? Or if you need variable-width, take the width as an arg and(x<<(32-n)) >> (32-n)
to sign-extend the low n bits? Where do you draw the line? \$\endgroup\$111
produce-3
instead of-1
? \$\endgroup\$111
is -1*(2^2) + 1*(2^1) + 1*(2^0) = -4 + 2 + 1 = -1. \$\endgroup\$