TL;DR: This is well known. Add extra bits by duplicating the existing bits as needed, instead of padding with zero bits. So, *abcde*_{2} → *abcdeabcdeab*_{2} for 5-to-12-bit conversion, for example.

Mathematically, to scale between *A*_{min} ≤ *A* ≤ *A*_{max} and *B*_{min} ≤ *B* ≤ *B*_{max} you use

*B* = *B*_{min} + (*A* - *A*_{min}) × (*B*_{max} - *B*_{min}) / (*A*_{max} - *A*_{min})

or, inversely,

*A* = *A*_{min} + (*B* - *B*_{min}) × (*A*_{max} - *A*_{min}) / (*B*_{max} - *B*_{min})

For *a* and *b* -bit unsigned integers, this simplifies to

*B* = *A* × (2^{b} - 1) / (2^{a} - 1)

Note: the scaling factor is not 2^{b-a} (that you would obtain with a pure binary shift), but (2^{b}-1)/(2^{a}-1) instead.

Duplicating and concatenating the bits in *A* yields a very good approximation, affecting only how the mathematically exact real value is effectively rounded to an integer value.

In C:

If *b* < *a*, we can use *B* = *A* >> (*a* - *b*); .

If *b* > *a* but *b* < 2*a*, we can use *B* = (*A* << (*b* - *a*)) | (*A* >> (2**a* - *b*)); .

If *b* ≥ 2*a* but *b* < 3*a*, we can use *B* = (*A* << (*b* - *a*)) | (*A* << (*b* - 2**a*)) | (*A* >> (3**a* - *b*)); .

And so on.

For example, to calculate a 3-bit unsigned integer *y* (0 ≤ *y* ≤ 7) from a 2-bit unsigned integer *x* (0 ≤ *x* ≤ 3), in C you can use

Code:

y = (x << 1) | (x >> 1);

To calculate a 12-bit unsigned integer *y* (0 ≤ *y* ≤ 4095) from a 5-bit unsigned integer *x* (0 ≤ *x* ≤ 31), in C you can use

Code:

y = (x << 7) | (x << 2) | (x >> 3);

To calculate a 8-bit unsigned integer *y* (0 ≤ *y* ≤ 255) from a 5-bit unsigned integer *x* (0 ≤ *x* ≤ 31), in C you can use

Code:

y = (x << 3) | (x >> 2);

For the cases when unsigned integer *y* has 2*n* bits and *x* has *n* bits,
which yields the exact same value as
when (0 ≤ *x* < 2^{n}).

On hardware architectures with a fast multiply operation with sufficiently wide integers and *b* several times *a*, it is better to approximate (2^{b}-1)/(2^{a}-1) by a multiplication by an integer constant and optionally a right shift, i.e. mathematically use factor (*C* / 2^{c}). It depends on the compiler (version), hardware, and particular *a* and *b*, though.