有没有办法在不使用任何关系运算符的情况下将大于等于 1 的整数转换为 1?

Is there a way to convert an integer to 1 if it is >= 1 without using any relational operator?

在我的程序中,我在循环中有如下语句。

y = (x >= 1)? 0:1;

但是,我想避免使用任何关系运算符,因为我想使用 SIMD 指令,并且不确定关系运算符是否可以很好地与 SIMD 配合使用。

我想要类似下面的内容。

a = some_operation(x) // a will be either 1 or 0
y = 1 - a

其中some_operation会将任何等于或大于1的数字转换为1,并将0转换为0。所以,我的问题是,是否有任何some_operation可以实现我的目的?

Is there a way to convert an integer to 1 if it is >= 1 without using any relational operator?

对于无符号整数你可以简单地做:

unsigned int i = 42; // ... or any other value > 0.
unsigned int j = !!i; // j is 1 here.

i = 0;
j = !!i; // j is 0 here.

更新:

对于有符号整数你可以做

int i = ...
int j = !!(i * !((1 << ((CHAR_BITS * sizeof i) - 1)) & i)); 

上面一行的结果是

  • 0 对于任何 i < 1
  • 1 对于任何 i >= 1

我不熟悉 C 或使用 SIMD 指令,但是如果 x 是一个正整数,你不能这样做吗?

y = (x == 0) ? 1 : 0;

假设您使用二进制补码,您可以做到这一点。 (使用 !!x 的另一个答案可能是也可能不是你要找的,这取决于计算机的指令集以及你为什么要避免关系运算符)

int x = 42; // or any integer

int test = x-1;
if(test & 1 << (CHAR_BIT * sizeof(int) -1))
{
   // integer negative or zero
}
else
{
   // integer positive
}

使用这个:y= 1/(1+e^(-10000*(x-0.995)))

这将得到 y = 0 for x <= 0.99y=1 for x>= 1

我不知道 SIMD 是什么,很可能有更好的方法来做到这一点。但我想,如果你不想使用条件,你可以根据你的标准使用 returns 0 或 1 的 sigmoid 函数。此函数将是您的 some_operation(x) 。请注意,此函数仅适用于带 2 位小数的数字。也就是说,输入 0.99 将 return 0,但输入 0.999 将 return 1。确保将数字 down 四舍五入到最接近的小数位数计算之前的数字。

如果有人感兴趣,我将在下面逐步介绍我的思考过程:

如果你想使用一个函数,而不是一个逻辑条件,它必须是连续的,根据定义,这意味着一些值不符合标准。但是,如果这些值在一个非常窄的范围内,并且您在数字之间的步长大于该窄范围,它就会起作用。

所以你可以使用 sigmoid 函数。 (将其输入到 wolfram alpha 中,以便查看每个更改)

  y =  1/(1+e^(-x))   

并将它向右移动一步,使其以 1 而不是零为中心。

  y = 1/(1+e^(-(x-1)))

那么可以通过增加权重来增加函数的斜率。

  y= 1/(1+e^(-10000*(x-1))) 

现在坡度真的非常非常陡。 但是如果我们输入 x=1,我们仍然会得到 y = 0.5。所以我们需要将 sigmoid 向左移动一点。

y= 1/(1+e^(-10000*(x-0.995))) 

现在,如果 x <= 0.99,我们将得到 y= 0,如果 x >= 1,我们将得到 y=1。 如果您想使用更精细的分辨率,则必须调整权重(在本例中为 10000)和中心点(在本例中为 0.995)。 我刚刚检查了 wolfram alpha 中的计算并迭代了有效的方法。如果您只使用两位小数,您可以使用低至 4000 的权重。

我确定有更好的方法来执行此操作,但这就是我要解决的方法。

#define INT_BITS (CHAR_BIT * sizeof(int))

int make_zero_or_one(int x) {
   return 1 - (((x-1) >> (INT_BITS-1)) & 1);
}

与其他答案一样,这依赖于 MSB 是整数中的符号位。函数 returns 0 对于所有 <= 0 的整数,否则为 1。如果 x-1 溢出,该函数将失败。

此实现在编译代码中没有分支。

我知道这个答案明确地与您的问题相矛盾,但是所有常见的 SIMD 架构都有 SIMD 比较(至少我所知道的)。

对于SSE2和int32参数有pcmpgtd(内在:_mm_cmpgt_epi32),假设你在__m128i x中有4个整数,你可以写

__m128i result = _mm_cmpgt_epi32(x, _mm_setzero_si128())

为每个 x>0(即 x>=1)获得 -1(即 0xFFFFFFFF),否则 0。如果你需要 1 而不是 -1 就写

__m128i y =  _mm_sub_epi32(_mm_setzero_si128(), result);