I'm trying to wrap my head around how I'd multiply a number from 0..4096 by 0.3 using just integers with shift operations and scaling, without dividing, in C. I'm new to this stuff and any input or step by step suggestions would be extremely helpful.
-
2I think he means any given number from the range `[0, 4096]` multiplied by 0.3 – Ferdinand Beyer Mar 23 '15 at 22:03
-
Well, first you need to start learning how floating point numbers work behind the scenes. In your case, `0.3 = 3/10`, so you first need to multiply by 3, then divide by 10, all using integers. – Alex Salauyou Mar 23 '15 at 22:04
-
I do mean from the range 0...4096. The idea is to scale up so both are integers and thereby avoid floating point operations. – Donald Hawken Mar 23 '15 at 22:04
-
9Multiply by 3 and then divide by 10. – Daniel Kamil Kozar Mar 23 '15 at 22:05
-
1This may help: http://stackoverflow.com/questions/11694546/divide-a-number-by-3-without-using-operators – Retired Ninja Mar 23 '15 at 22:05
-
I understand that the decimal parts of a float are 2^-1, 2^-2, 2^-3, 2^-4 etc. – Donald Hawken Mar 23 '15 at 22:05
-
2(a*9830 + 16384)>>15 – user3528438 Mar 23 '15 at 22:08
-
3@DanielKamilKozar I upvoted your comment then noticed *'without dividing in C'*. – abligh Mar 23 '15 at 22:20
-
I hope by "scaling" you meant "multiplication", since that's what most of the answers assume. – Mark Ransom Mar 23 '15 at 22:50
-
@abligh I upvoted his answer then noticed your comment, and *then* noticed '_without dividing in C_'. – OJFord Mar 24 '15 at 02:09
-
@KenWhite. going to floating point doesn't do anything to solve the problem of 0.3 not being able to be represented precisely. anything that can be done with finite-precision floating-point binary numbers can also be done with fixed-point binary numbers. just follow the same rules. – robert bristow-johnson Mar 24 '15 at 02:28
-
What's the format of the numbers? Fixed-point? Floating-point? Fixed-point decimal? – phuclv Mar 24 '15 at 11:56
5 Answers
Multiplying by 0.3 is the same as multiplying by (0.3*2^n)
, then dividing by 2^n
. The second stage is equivalent to a shift right of n
.
But what's the best value of n
?
To find this, take your largest integer and find the largest value of n
such that you can multiply by (0.3*2^n)
without an overflow. With 64 bit unsigned integers and 4096 as the maximum value, you need
0.3*2^n <= 2^(64-12)
or
0.3 <= 2^(64-12-n)
That inequality has maximum n
when the RHS is equal to 0.5, so
2^-1 = 2^(64-12-n)
so -1 = 64-12-n
, n = 64-12+1 = 53
.
So the answer is multiply by 2^53*0.3
then shift right by 53, i.e.
/* designed to work with input values 0 .. 4096 only */
uint64_t
multiplyby0_3 (uint64_t x)
{
return (x * 2702159776422297ULL) >> 53;
}
To check that doesn't overflow and we've got the best n
, from bc
:
2702159776422297*4096 = 11068046444225728512
2^64 = 18446744073709551616
IE it won't overflow, but if we multiplied it by 2 again, it would.
For 32 bit integers, the answer is multiply by 2^21*0.3
then shift right by 21, i.e.
/* designed to work with input values 0 .. 4096 only */
uint32_t
multiplyby0_3 (uint32_t x)
{
return (x * 629146U) >> 21;
}
And finally, you can decompose any multiply into a number of additions by looking at the binary 1
's in the multiplier. So you permit 'scaling' and I assumed that meant multiply. If not, here is the 32 bit version (64 bit version left as an exercise for the reader) exploiting the fact that 629146
is 10011001100110011010
(a neat pattern due to the recurring binary fraction). We'll round the other way and use 10011001100110011001
instead.
/* designed to work with input values 0 .. 4096 only */
uint32_t
multiplyby0_3 (uint32_t x)
{
uint32_t y;
x += x<<3; /* * 1001 */
y = x<<4 + x; /* * 10011001 */
y += y<<8; /* * 1001100110011001 */
y += x<<16; /* * 10011001100110011001 */
return y >> 21;
}

- 24,573
- 4
- 47
- 84
-
Using that many bits is overkill - since the input range is only ~ 12 bits then you should be able to do this with full accuracy well within a 32 bit intermediate value. – Paul R Mar 23 '15 at 22:19
-
1@duskwuff I don't think that's right. The highest value possible (**given the input is limited to 0 .. 4096**) is 4096, so the bit in the brackets is at most ``2702159776422297 * 4096 = 11068046444225728512` which fits comfortably (just) into 64 bits. I'll stick a comment on the code samples. – abligh Mar 23 '15 at 22:23
-
@PaulR isn't that what the 32 bit example does? Yes, I realise it's overkill but I wanted to teach OP how to work it out in the general case. Proving a lower number of bits will never give a different answer is more work in any case! – abligh Mar 23 '15 at 22:28
-
Sure - if you change `629146ULL` to `629146U` so that you're only using 32 bit arithmetic then it's fine. – Paul R Mar 23 '15 at 22:36
-
@abligh gets the up arrow. the number of bits to commit to 0.3 is the number of bits in your unsigned word (64 or 32) minus 12 (since 2^12 = 4096). so you want 0.3 to be represented as 0.3 * 2^52 or 0.3 * 2^20, multiply your value, and (for rounding to nearest) add either 2^51 and shift right 52 bits or add 2^19 and shift right by 20 bits. – robert bristow-johnson Mar 24 '15 at 02:36
If you have fast integer multiplication, but you don't have integer division, you can get a reasonable approximation by multiplying by 1229, then shifting right by 12 bits. For instance:
>> 100 * 1229
122900
>> 122900 >> 12
30
This works because 1229 is about 0.3 * 1 << 12
. The "real" value is 1228.8, so the estimate will be high by 1 in some cases (68 out of 4097 values). It will never be off by more than 1, though.
-
4
-
-
@EOF: 122900 is only 17 bits, so with 32 bit ints and a 12 bit input range this should be fine. – Paul R Mar 23 '15 at 22:12
-
2@EOF The maximum intermediate result, given the range specified in the question, is `1229 * 4096 = 5033984`. That's well within the range of a 32-bit integer; if you need to stay within 16 bits, that's a bit tougher. – Mar 23 '15 at 22:12
-
Right, but this doesn't appear to be accurate for all input values. I took it that OP was trying to find a way of producing the same thing as multiplying an integer by 0.3 (rounding the result), and this would appear to get it wrong occasionally. You need a few more bits (possibly not as many as in my answer!) – abligh Mar 23 '15 at 22:32
-
@abligh It's pretty accurate… There's no number between 0 and 4096 for which `(x * 1229) >> 12` differs from `x * 0.3` by more than 0.9. – Mar 23 '15 at 23:16
Using a classic hack for divu10()
below. I prefer the *n and shift approach, but thought I offer another POV.
unsigned mult3tenths(unsigned x) {
return divu10(x*3);
}
unsigned divu10(unsigned n) {
unsigned q, rem;
q = (n >> 1) + (n >> 2);
q = q + (q >> 4);
q = q + (q >> 8);
q = q + (q >> 16);
q = q >> 3;
rem = n - q*10;
return q + ((rem + 6) >> 4);
}

- 143,097
- 13
- 135
- 256
-
1neat, though I'm saddened the second line didn't read `div10(x+(x<<1))` and later `rem=n-(q<<3)-(q<<1)` just to eliminate all multiplication as well :-) – abligh Mar 23 '15 at 22:34
-
1@abligh Suspect a good compiler will take `x*3` or `x+(x<<1)` and make the same code. – chux - Reinstate Monica Mar 23 '15 at 22:43
-
yeah it will indeed. But for a laugh, I found an even shorter way without explicit multiplication and added it to my answer :-) – abligh Mar 23 '15 at 23:21
I know that this is not a coding example, sorry about that, but if the set is so small (range [0;4096]), why not create a block of results and use a pointer to extract values? It will greatly reduce the GPU cycles, since there are not memory restrictions.

- 45
- 3
-
2
-
Yeah... It slips from my mind.... That integer restriction makes me think at GPU computing, ATI chips and bitcoin mining... My mistake :) – Tinel Barb Mar 24 '15 at 09:58
Simply tried a lot of combinations of the form (A*x + B) >> n
with the below test code and came up with:
// Scale by 4915, then shift 14.
int Mult3Tenths(int x) {
return (x*4915 + 0) >> 14; // Use 4915L if `int` is 16 bit.
}
Test code
#define N3 (4096)
int main(void) {
int target[N3 + 1];
unsigned i;
for (i = 0; i <= N3; i++) {
target[i] = 0.3 * i;
}
// form (A*x + B) >> n
int A, B, n;
int besti = 0;
for (n = 0; n < 31; n++) {
int Amin = ((N3 * 0.3 - 1) * (1 << n) - (1 << n)) / N3 - 1;
int Amax = ((N3 * 0.3 + 1) * (1 << n) + (1 << n)) / N3 + 1;
for (A = Amin; A <= Amax; A++) {
int Bmax = 1 << n;
for (B = -Bmax; B <= Bmax; B++) {
for (i = 0; i <= N3; i++) {
int y = (A * i + B) >> n;
if (y != target[i])
break;
if (i > besti) {
besti = i;
if (i == N3) {
printf("i:%i A:%d B:%d n:%d\n", i, A, B, n);
printf("!!!\n");
exit(0);
}
}
}
}
}
}
printf("???\n");
return 0;
}

- 143,097
- 13
- 135
- 256