没有逻辑运算符的舍入整数除法

时间:2016-01-20 17:18:33

标签: c++ c rounding

我想要一个功能

int rounded_division(const int a, const int b) { 
    return round(1.0 * a/b); 
}

所以我们有,例如,

rounded_division(3, 2) // = 2
rounded_division(2, 2) // = 1
rounded_division(1, 2) // = 1
rounded_division(0, 2) // = 0
rounded_division(-1, 2) // = -1
rounded_division(-2, 2) // = -1
rounded_division(-3, -2) // = 2

或者在代码中,ab是32位有符号整数:

int rounded_division(const int a, const int b) {
    return ((a < 0) ^ (b < 0)) ? ((a - b / 2) / b) : ((a + b / 2) / b);
}

这里有一个棘手的部分:如何实现这个人有效(不使用更大的64位值)和没有逻辑运算符,例如?:&&,......?它有可能吗?

我之所以想避免使用逻辑运算符,因为我必须实现此函数的处理器没有条件指令(more about missing conditional instructions on ARM.)。

8 个答案:

答案 0 :(得分:8)

a/b + a%b/(b/2 + b%2)工作得很好 - 没有失败的十亿+测试用例。它符合所有OP的目标:当long long被定义时,没有溢出,没有int,没有分支,适用于a/b的整个范围。

没有32位依赖。如果使用C99或更高版本,则没有实现行为限制。

int rounded_division(int a, int b) {
  int q = a / b;
  int r = a % b;
  return q + r/(b/2 + b%2);
}

这适用于2的补码,1的补码和符号幅度,因为所有操作都是数学运算。

答案 1 :(得分:3)

这个怎么样:

int rounded_division(const int a, const int b) {
    return (a + b/2 + b * ((a^b) >> 31))/b;
}
如果(a ^ b) >> 31-1有不同的符号,则{p> a应评估为b,否则为0,假设int有32位且最左边是符号位。

修改

正如@chux在他的评论中指出的那样,由于整数除法,这种方法是错误的。这个新版本的评估与OP的示例相同,但包含更多操作。

int rounded_division(const int a, const int b) {
    return (a + b * (1 + 2 * ((a^b) >> 31)) / 2)/b;
}

然而,此版本仍未考虑溢出问题。

答案 2 :(得分:1)

怎么样?
  ...
  return ((a + (a*b)/abs(a*b) * b / 2) / b);
}

没有溢出:

  ...
  return ((a + ((a/abs(a))*(b/abs(b))) * b / 2) / b);    
}

答案 3 :(得分:1)

我想出的代码可以在ARM M0上使用(无浮点,缓慢除法)。 它仅使用一个除法指令,没有任何条件,但是如果分子+(分母/ 2)> INT_MAX,则会溢出。

ARM M0上的周期数= 7个周期+除法(M0没有除法指令,因此取决于工具链)。

int32_t Int32_SignOf(int32_t val)
{
    return (+1 | (val >> 31)); // if v < 0 then -1, else +1
}

uint32_t Int32_Abs(int32_t val)
{
    int32_t tmp = val ^ (val >> 31);
    return (tmp - (val >> 31));

    // the following code looks like it should be faster, using subexpression elimination
    // except on arm a bitshift is free when performed with another operation,
    // so it would actually end up being slower
    // tmp = val >> 31;
    // dst = val ^ (tmp);
    // dst -= tmp;
    // return dst;
}

int32_t Int32_DivRound(int32_t numerator, int32_t denominator)
{
    // use the absolute (unsigned) demominator in the fudge value
    // as the divide by 2 then becomes a bitshift
    int32_t  sign_num  = Int32_SignOf(numerator);
    uint32_t abs_denom = Int32_Abs(denominator);
    return (numerator + sign_num * ((int32_t)(abs_denom / 2u))) / denominator;
}

答案 4 :(得分:0)

这是您可能会使用的粗略方法。如果操作a * b&lt;操作a * b&lt;使用掩码来应用某些东西。 0.

请注意,我没有对此进行适当的测试。

int function(int a, int b){
    int tmp = float(a)/b + 0.5;
    int mask = (a*b) >> 31; // shift sign bit to set rest of the bits

    return tmp - (1 & mask);//minus one if a*b was < 0
}

答案 5 :(得分:0)

以下sign(int a)符合OP的无分支要求 - 如果将nabs(int a)cmp_le(int a, int b)sign()视为非分支。有关如何在没有比较运算符的情况下执行rounded_division_test1()的提示,请参阅here。这些辅助函数可以在没有显式调用的情况下滚动到a/b

该代码演示了正确的功能,可用于测试各种答案。定义#include <limits.h> #include <math.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> int nabs(int a) { return (a < 0) * a - (a >= 0) * a; } int sign(int a) { return (a > 0) - (a < 0); } int cmp_le(int a, int b) { return (a <= b); } int rounded_division_test1(int a, int b) { int q = a / b; int r = a % b; int flag = cmp_le(nabs(r), (nabs(b) / 2 + nabs(b % 2))); return q + flag * sign(b) * sign(r); } // Alternative that uses long long int rounded_division_test1LL(int a, int b) { int c = (a^b)>>31; return (a + (c*2 + 1)*1LL*b/2)/b; } // Reference code int rounded_division(int a, int b) { return round(1.0*a/b); } int test(int a, int b) { int q0 = rounded_division(a, b); //int q1 = function(a,b); int q1 = rounded_division_test1(a, b); if (q0 != q1) { printf("%d %d --> %d %d\n", a, b, q0, q1); fflush(stdout); } return q0 != q1; } void tests(void) { int err = 0; int const a[] = { INT_MIN, INT_MIN + 1, INT_MIN + 1, -3, -2, -1, 0, 1, 2, 3, INT_MAX - 1, INT_MAX }; for (unsigned i = 0; i < sizeof a / sizeof a[0]; i++) { for (unsigned j = 0; j < sizeof a / sizeof a[0]; j++) { if (a[j] == 0) continue; if (a[i] == INT_MIN && a[j] == -1) continue; err += test(a[i], a[j]); } } printf("Err %d\n", err); } int main(void) { tests(); return 0; } 时,此答案不会溢出。

null == ptr

答案 6 :(得分:0)

因为函数似乎是对称的sign(a/b)*floor(abs(a/b)+0.5)

怎么样

答案 7 :(得分:0)

让我给出我的贡献:

怎么样:

int rounded_division(const int a, const int b) {
    return a/b + (2*(a%b))/b;
}

没有分支,没有逻辑运算符,只有数学运算符。但如果b大于INT_MAX / 2或小于INT_MIN / 2,则可能失败。

但是如果允许64位计算32位轮次。它不会失败

int rounded_division(const int a, const int b) {
    return a/b + (2LL*(a%b))/b;
}