In C bits, multiply by 3 and divide by 16

别等时光非礼了梦想. 提交于 2019-11-30 07:45:56
Cole

For this question you need to worry about the lost bits before your division (obviously). Essentially, if it is negative then you want to add 15 after you multiply by 3. A simple if statement (using your operators) should suffice.

I am not going to give you the code but a step by step would look like,

x = x*3

get the sign and store it in variable blarg.

have another variable hold x + 15;

Set up an if statement so that if x is negative it uses that added 15 and if not then it uses the regular number (times 3 which we did above).

Then divide by 16 which you already showed you know how to do. Good luck!

This seems to work (as long as no overflow occurs):

((num<<2)+~num+1)>>4

Try this JavaScript code, run in console:

for (var num = -128; num <= 128; ++num) {
  var a = Math.floor(num * 3 / 16);
  var b = ((num<<2)+~num+1)>>4;
  console.log(
    "Input:", num,
    "Regular math:", a,
    "Bit math:", b,
    "Equal: ", a===b
  );
}

The Maths

When you divide a positive integer n by 16, you get a positive integer quotient k and a remainder c < 16:

    (n/16) = k + (c/16).

(Or simply apply the Euclidan algorithm.) The question asks for multiplication by 3/16, so multiply by 3

    (n/16) * 3 = 3k + (c/16) * 3.

The number k is an integer, so the part 3k is still a whole number. However, int arithmetic rounds down, so the second term may lose precision if you divide first, And since c < 16, you can safely multiply first without overflowing (assuming sizeof(int) >= 7). So the algorithm design can be

    (3n/16) = 3k + (3c/16).

The design

  • The integer k is simply n/16 rounded down towards 0. So k can be found by applying a single AND operation. Two further operations will give 3k. Operation count: 3.
  • The remainder c can also be found using an AND operation (with the missing bits). Multiplication by 3 uses two more operations. And shifts finishes the division. Operation count: 4.
  • Add them together gives you the final answer.

Total operation count: 8.

Negatives

The above algorithm uses shift operations. It may not work well on negatives. However, assuming two's complement, the sign of n is stored in a sign bit. It can be removed beforing applying the algorithm and reapplied on the answer.

  • To find and store the sign of n, a single AND is sufficient.
  • To remove this sign, OR can be used.
  • Apply the above algorithm.
  • To restore the sign bit, Use a final OR operation on the algorithm output with the stored sign bit.

This brings the final operation count up to 11.

what you can do is first divide by 4 then add 3 times then again devide by 4.

3*x/16=(x/4+x/4+x/4)/4

with this logic the program can be

main()
{
   int x=0xefffffff;
   int y;
   printf("%x",x);
   y=x&(0x80000000);
   y=y>>31;
   x=(y&(~x+1))+(~y&(x));
   x=x>>2;
   x=x&(0x3fffffff);
   x=x+x+x;
   x=x>>2;
   x=x&(0x3fffffff);
    x=(y&(~x+1))+(~y&(x));
   printf("\n%x %d",x,x);
}

AND with 0x3fffffff to make msb's zero. it'l even convert numbers to positive. This uses 2's complement of negative numbers. with direct methods to divide there will be loss of bit accuracy for negative numbers. so use this work arround of converting -ve to +ve number then perform division operations.

Note that the C99 standard states in section section 6.5.7 that right shifts of signed negative integer invokes implementation-defined behavior. Under the provisions that int is comprised of 32 bits and that right shifting of signed integers maps to an arithmetic shift instruction, the following code works for all int inputs. A fully portable solution that also fulfills the requirements set out in the question may be possible, but I cannot think of one right now.

My basic idea is to split the number into high and low bits to prevent intermediate overflow. The high bits are divided by 16 first (this is an exact operation), then multiplied by three. The low bits are first multiplied by three, then divided by 16. Since arithmetic right shift rounds towards negative infinity instead of towards zero like integer division, a correction needs to be applied to the right shift for negative numbers. For a right shift by N, one needs to add 2N-1 prior to the shift if the number to be shifted is negative.

#include <stdio.h>
#include <stdlib.h>

int ref (int a)
{
  long long int t = ((long long int)a * 3) / 16;
  return (int)t;
}

int main (void)
{
  int a, t, r, c, res;
  a = 0;
  do {
    t = a >> 4;         /* high order bits */
    r = a & 0xf;        /* low order bits */
    c = (a >> 31) & 15; /* shift correction. Portable alternative: (a < 0) ? 15 : 0 */
    res = t + t + t + ((r + r + r + c) >> 4);
    if (res != ref(a)) {
      printf ("!!!! error a=%08x  res=%08x  ref=%08x\n", a, res, ref(a));
      return EXIT_FAILURE;
    }
    a++;
  } while (a);
  return EXIT_SUCCESS;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!