【问题标题】:Make a function to return 1 if x <= y, using only bitwise operators [duplicate]如果 x <= y,则创建一个函数以返回 1,仅使用位运算符 [重复]
【发布时间】:2018-04-21 03:21:14
【问题描述】:

这是家庭作业,我正在努力解决这个问题,而且我能看到的至少有一半的班级都在这里。无论如何。我需要创建一个returns 1 if x &lt;= y else return 0 的函数。

我们只能使用:! ~ &amp; ^ | + &lt;&lt; &gt;&gt;,最大操作数是:24 所有整数都必须是signed,并且不能对我创建的或 C 提供的任何其他函数进行任何调用。我们可以假设所有整数都是32 bits,并且它们必须有符号。

我不确定从哪里开始使用此功能。到目前为止,我有以下内容:

int isLessOrEqual(int x, int y) {
    int diff = (y+~x+1); //The same as y-x
    int diffSign = (diff>>31) & 1; //if negative, this will be 1. Else it'll be 0.
    return !diffSign;
}

这似乎可行。但它不适用于某些输入(根据进行评分的程序)。

这是我在网上找到的一个功能,但我不想复制它。我想了解它,看看为什么它有效而我的无效。看来我的代码溢出时不起作用,需要处理一下。

int isLessOrEqual(int x, int y) {
  int sign, isLess, dif, equal, isLessorEqual;

  sign = x ^ y;
  isLess = ~sign;
  dif = y + (~x + 1);


  equal = !dif;

  sign = sign & x;

  dif = ~dif;

   isLess = isLess & dif;
   isLess = isLess | sign;
   isLess = isLess & (1 << 31);

   isLessorEqual = (!!isLess) | (equal);

   return isLessorEqual;  
}

如果有人可以帮助我理解这些功能的不同之处或如何处理溢出,那将不胜感激,这也是我所要求的。我很接近,只是无法弄清楚这个溢出的东西

【问题讨论】:

  • 您需要使用int
  • @AnttiHaapala 是的。必须是有符号的 int。无法调用 C 中的任何其他函数或我创建的函数。
  • 那么这是非常误导,因为许多未定义和实现定义的行为。最重要的是,您假设 intdiff &gt;&gt; 31 中的宽度。
  • @Brystephor 是的,但是 C int 数学并不总是安全的,所以这就是 为什么 分配错误的原因。例如(y+~x+1) 可能导致有符号溢出(如果例如yINT_MINxINT_MAX)。如果发生未定义的行为,优化编译器可能会注意到某些条件可能 - 因此优化它们...
  • 为什么你说它不适用于y=5x=-6?看起来确实如此:ideone.com/7ZhVFV

标签: c bit-manipulation


【解决方案1】:

这里的人似乎在为你做功课,而不是按照你的要求分析工作示例。

不是我将如何编写函数,实际上看起来像是混淆代码。这是你的作业,所以我不会给出完整的解决方案。 (预计到达时间:嗯,你已经看到了答案。)

让我们稍微改写成静态的单一赋值,并使用较少误导的名称:

#include <limits.h> // For CHAR_BIT

int isLessOrEqual(const int x, const int y) {
   const int different_bits = x ^ y;      // sign = x ^ y;
   const int same_bits = ~different_bits; // isLess = ~sign;
   const int y_minus_x = y + (~x + 1);    // dif = y + (~x + 1);

   const int x_equals_y = !y_minus_x;           // equal = !dif; 
   const int bits_x_not_y = different_bits & x; // sign = sign & x;

   const int y_minus_x_inverted = ~y_minus_x;   // dif = ~dif;
   const int same_bits_not_in_y_minus_x =
     same_bits & y_minus_x_inverted             // isLess = isLess & dif;
   const int explained_below =
      same_bits_not_in_y_minus_x | bits_x_not_y; // isLess = isLess | sign;
   static const int sign_bit = 1 << (sizeof(y_minus_x)*CHAR_BIT - 1);
   const int truthy_iff_less =
     explained_below & sign_bit; // isLess = isLess & (1 << 31);

   const int is_less_or_equal =
     (!!truthy_iff_less) | x_equals_y; // isLessorEqual = (!!isLess) | (equal);

   return is_less_or_equal;  
}

我确实做了一个修复:发布的代码假定 int 正好是 32 位宽,这并不总是正确的。我在编译时计算符号位的正确掩码。

通过这种方式去混淆,我们可以看到我们可以跳过将y_minus_x 转换为explained_below 的操作。更仔细地查看explained_below,我们看到如果以下任何一项为真,则它的每一位都被设置:在所有 x、y 和 yx 中都清晰,在 x 和 y 中设置但在 yx 中清晰,或者它设置在 x 但不是 y。由于我们只关心符号位,因此当 x 和 y 均为正且 yx >= 0、x 和 y 均为负且 yx >= 0 或 x 0 时,我们会得到一个真值。 /p>

因此,此代码与您的不同之处在于,它检查溢出,当 yx 看起来为负时,虽然 x 为负且 y 为正,或者当 yx 看起来为正但 x 为正且 y 为负时检查下溢。

对此有更简单的解决方案。如果您假设 xy 无论如何都只有 32 位长,只需将它们转换为 long long int。它不再需要 64 位 CPU 并且永远不会溢出或下溢。但是,如果您使用的是原生字长,您仍然可以减少其中的一些操作。您尤其不需要单独计算相等性,并且可以通过更少的反转来解决。记住德摩根的法律!

剧透

既然其他人都为你发布了答案,而且你已经看过算法,我想我也不妨也发布一个。除了编码风格和可移植性的一些更改外,它是相似的(不假定类型正好是 32 位宽,不假定有符号溢出的行为类似于无符号溢出等),尽管它仍然假定二进制补码算术。

#include <assert.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

typedef int_fast32_t t;
typedef uint_fast32_t ut;

static const ut sign_bit = (ut)1 << (sizeof(ut)*CHAR_BIT - 1);
#define T_MIN INT_FAST32_MIN
#define T_MAX INT_FAST32_MAX

int lte( const t x, const t y )
{
  const ut y_minus_x = (ut)y + ~(ut)x + 1; // Assumes two’s-complement math.
  const ut different_bits = (ut)x ^ (ut)y;
  const ut x_not_y = (ut)x & ~(ut)y;
  const ut lte_without_overflow = ~(y_minus_x | different_bits);
  const ut truthy_result = (lte_without_overflow | x_not_y) & sign_bit;
  return !!truthy_result;
}

int print_testcase( const t x, const t y )
{
  static const char op_lte[] = "<=";
  static const char op_gt[] = ">";

  assert( lte(x, y) == (x <= y) );
  return printf( "%lld %s %lld.\n",
                 (long long int)x,
                 lte(x, y) ? op_lte : op_gt,
                 (long long int)y
               );
}

int main(void)
{
  static const t cases[][2] = {
    {1,1}, {-1,-1}, {2,1}, {1,2}, {-2,-1}, {-1, -2}, {2, -1}, {1, -2},
    {T_MAX, T_MIN}, {T_MIN, T_MAX}
  };
  static const ptrdiff_t ncases = sizeof(cases)/sizeof(cases[0]);

  for ( ptrdiff_t i = 0; i < ncases; ++i ) {
    print_testcase( cases[i][0], cases[i][1] );
  }

  return EXIT_SUCCESS;
}

【讨论】:

  • @hvd 感谢您在我的回答中指出一个误导性的句子。已更正。
  • 这是我一直在寻找的。我已经提交了我的作业等,但我想了解这些事情是如何工作的,以及为什么要完成这些事情。如果我想要答案,我可以在谷歌快速搜索中找到它。
【解决方案2】:

这是我发现的工作。这也是溢出的原因。需要第二种方法来检查。

int isLessOrEqual(int x, int y) {
  int xSign, ySign, diffSign, answer;
  xSign = (x >> 31) & 1; //1 if x < 0
  ySign = (y>>31) & 1; //1 if y < 0
  diffSign = ((y+~x+1)>>31)&1; //1 if y-x < 0

  /*if xSign = 1, and y=0, then x must be less then y.
  if x&y are both positive, and the difference is negative, then
  y-x must be 0.*/
  answer = ((!ySign) & xSign) | ((!(xSign^ySign)) & !diffSign);
  return answer;
}

【讨论】:

  • (x &gt;&gt; 31) &amp; 1 依赖于实现定义的行为。 (x &amp; 0x80000000) == 0x80000000 会更好
【解决方案3】:

x-y 只有在 x 和 y 的符号相反时才会溢出,所以直接处理这些情况。也不需要用 1 屏蔽符号位。

int isLessOrEqual(int x, int y) {
    int diff = (y+~x+1);
    int diffSign = diff>>31; 
    int xsign = x >> 31;
    int ysign = y >> 31;
    int forcetrue = xsign & ~ysign;
    int forcefalse = xsign | ~ysign;

    return !!(forcefalse&(forcetrue|~diffSign));
}

当然,当差异溢出时,这仍然具有未定义的行为。

【讨论】:

  • 是的。溢出的东西正在杀死这些测试中的所有这些功能。
  • @user3386109:是的,昨晚有点累。显然,还需要一个任期。也有倒退的感觉。我认为已修复
  • @Nemo 是的,可行:)
  • 不,当x为负,y为正(在负侧)​​时也会溢出
  • @LuisColorado:是的,这与 user3386109 在评论中提出的观点相同(已删除)。作为回应,我修复了代码,但没有修复随附的文本。现在我也修复了文本
【解决方案4】:
int isLessOrEqual(int x, int y) {
    int diff = x + ~y + 1; // diff = x-y
    int isNegative = (diff>>31) & 1; //if diff < 0, isNegative = 1
    int isZero = !(diff & (~1+1)); // if diff == 0, isZero = 1

    int xSign = (x>>31)&1;
    int ySign = (y>>31)&1;

    return isNegative | isZero | (xSign & (!ySign) & (!isNegative));
}

【讨论】:

  • 如果 diff == 2 会怎样?
  • int isZero = diff & ~1; // 如果 diff == 0,isZero = 1 嗯? 0 按位与任何东西都为 0,对吧?
  • 对不起。错误。已编辑。
  • 这并没有通过所有测试。
  • 我编辑了代码。现在可以使用了吗?
猜你喜欢
  • 2011-08-28
  • 2019-03-18
  • 2020-06-26
  • 1970-01-01
  • 2011-06-23
  • 2019-05-27
  • 2012-05-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多