In C bits, multiply by 3 and divide by 16

后端 未结 5 600
孤独总比滥情好
孤独总比滥情好 2020-12-29 17:46

A buddy of mine had these puzzles and this is one that is eluding me. Here is the problem, you are given a number and you want to return that number times 3 and divided by 1

相关标签:
5条回答
  • 2020-12-29 17:49

    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;
    }
    
    0 讨论(0)
  • 2020-12-29 17:51

    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 foo.

    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!

    0 讨论(0)
  • 2020-12-29 18:00

    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.

    0 讨论(0)
  • 2020-12-29 18:03

    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
      );
    }
    
    0 讨论(0)
  • 2020-12-29 18:11

    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.

    0 讨论(0)
提交回复
热议问题