What's the fastest way to divide an integer by 3?

后端 未结 12 2093
天涯浪人
天涯浪人 2020-11-29 03:40
int x = n / 3;  // <-- make this faster

// for instance

int a = n * 3; // <-- normal integer multiplication

int b = (n << 1) + n; // <-- potentiall         


        
相关标签:
12条回答
  • 2020-11-29 03:49

    There is a faster way to do it if you know the ranges of the values, for example, if you are dividing a signed integer by 3 and you know the range of the value to be divided is 0 to 768, then you can multiply it by a factor and shift it to the left by a power of 2 to that factor divided by 3.

    eg.

    Range 0 -> 768

    you could use shifting of 10 bits, which multiplying by 1024, you want to divide by 3 so your multiplier should be 1024 / 3 = 341,

    so you can now use (x * 341) >> 10
    (Make sure the shift is a signed shift if using signed integers), also make sure the shift is an actually shift and not a bit ROLL

    This will effectively divide the value 3, and will run at about 1.6 times the speed as a natural divide by 3 on a standard x86 / x64 CPU.

    Of course the only reason you can make this optimization when the compiler cant is because the compiler does not know the maximum range of X and therefore cannot make this determination, but you as the programmer can.

    Sometime it may even be more beneficial to move the value into a larger value and then do the same thing, ie. if you have an int of full range you could make it an 64-bit value and then do the multiply and shift instead of dividing by 3.

    I had to do this recently to speed up image processing, i needed to find the average of 3 color channels, each color channel with a byte range (0 - 255). red green and blue.

    At first i just simply used:

    avg = (r + g + b) / 3;

    (So r + g + b has a maximum of 768 and a minimum of 0, because each channel is a byte 0 - 255)

    After millions of iterations the entire operation took 36 milliseconds.

    I changed the line to:

    avg = (r + g + b) * 341 >> 10;

    And that took it down to 22 milliseconds, its amazing what can be done with a little ingenuity.

    This speed up occurred in C# even though I had optimisations turned on and was running the program natively without debugging info and not through the IDE.

    0 讨论(0)
  • 2020-11-29 03:49

    For 64 bit numbers:

    uint64_t divBy3(uint64_t x)
    {
        return x*12297829382473034411ULL;
    }
    

    However this isn't the truncating integer division you might expect. It works correctly if the number is already divisible by 3, but it returns a huge number if it isn't.

    For example if you run it on for example 11, it returns 6148914691236517209. This looks like a garbage but it's in fact the correct answer: multiply it by 3 and you get back the 11!

    If you are looking for the truncating division, then just use the / operator. I highly doubt you can get much faster than that.

    Theory:

    64 bit unsigned arithmetic is a modulo 2^64 arithmetic. This means for each integer which is coprime with the 2^64 modulus (essentially all odd numbers) there exists a multiplicative inverse which you can use to multiply with instead of division. This magic number can be obtained by solving the 3*x + 2^64*y = 1 equation using the Extended Euclidean Algorithm.

    0 讨论(0)
  • 2020-11-29 03:50

    See How To Divide By 3 for an extended discussion of more efficiently dividing by 3, focused on doing FPGA arithmetic operations.

    Also relevant:

    • Optimizing integer divisions with Multiply Shift in C#
    0 讨论(0)
  • 2020-11-29 03:52

    Easy computation ... at most n iterations where n is your number of bits:

    uint8_t divideby3(uint8_t x)
    {
      uint8_t answer =0;
      do
      {
        x>>=1;
        answer+=x;
        x=-x;
      }while(x);
      return answer;
    }
    
    0 讨论(0)
  • 2020-11-29 03:53

    What if you really don't want to multiply or divide? Here is is an approximation I just invented. It works because (x/3) = (x/4) + (x/12). But since (x/12) = (x/4) / 3 we just have to repeat the process until its good enough.

    #include <stdio.h>
    
    void main()
    {
        int n = 1000;
        int a,b;
        a = n >> 2;
        b = (a >> 2);
        a += b;
        b = (b >> 2);
        a += b;
        b = (b >> 2);
        a += b;
        b = (b >> 2);
        a += b;
        printf("a=%d\n", a);
    }
    

    The result is 330. It could be made more accurate using b = ((b+2)>>2); to account for rounding.

    If you are allowed to multiply, just pick a suitable approximation for (1/3), with a power-of-2 divisor. For example, n * (1/3) ~= n * 43 / 128 = (n * 43) >> 7.

    This technique is most useful in Indiana.

    0 讨论(0)
  • 2020-11-29 03:53

    I don't know if it's faster but if you want to use a bitwise operator to perform binary division you can use the shift and subtract method described at this page:

    • Set quotient to 0
    • Align leftmost digits in dividend and divisor
    • Repeat:
      • If that portion of the dividend above the divisor is greater than or equal to the divisor:
        • Then subtract divisor from that portion of the dividend and
        • Concatentate 1 to the right hand end of the quotient
        • Else concatentate 0 to the right hand end of the quotient
      • Shift the divisor one place right
    • Until dividend is less than the divisor:
    • quotient is correct, dividend is remainder
    • STOP
    0 讨论(0)
提交回复
热议问题