C语言 有没有一种方法可以不使用任何关系运算符而将大于等于1的整数转换为1?

gkl3eglg  于 2023-03-07  发布在  其他
关注(0)|答案(7)|浏览(248)

在我的程序中,我有一个类似下面的语句,在一个循环中。

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可以实现我的目的?

piwo6bdm

piwo6bdm1#

#define INT_BITS (CHAR_BIT * sizeof(int))

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

和其他答案一样,这依赖于整数的符号位MSB,对于所有整数〈= 0,函数返回0,否则返回1,如果x-1溢出,函数将失败。
此实现在编译代码中没有分支。

sq1bmfud

sq1bmfud2#

有没有一种方法可以不使用任何关系运算符而将大于等于1的整数转换为1?
对于 * unsigned * 整数,您可以简单地执行以下操作:

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.

更新:
对于 * signed * 整数,您可以执行以下操作

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

以上行的结果为

  • 0用于任何i < 1
  • 1用于任何i >= 1
rfbsl7qr

rfbsl7qr3#

假设你使用2的补码,你可以做到这一点。(使用!!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
}
b1zrtrql

b1zrtrql4#

我知道这个答案与你的问题明显矛盾,但是在所有常见的SIMD架构上都有SIMD比较(至少我知道的是这样)。
对于SSE2和int32参数,存在pcmpgtd(固有:_mm_cmpgt_epi32),假设__m128i x中有4个整数,可以写为

__m128i result = _mm_cmpgt_epi32(x, _mm_setzero_si128())

要为每个x>0(即x>=1)和0获取-1(即0xFFFFFFFF),如果需要1而不是-1,只需写入

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

ntjbwcob5#

整数任意数到1 = 0||任意表达式
有点依赖于编译器,但是嘿;- )

kse8i1jr

kse8i1jr6#

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

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

u1ehiz5o7#

使用这个:y= 1/(1+e^(-10000*(x-0.995)))
这将给予y = 0 for x <= 0.99y=1 for x>= 1
我不知道SIMD是什么,很可能有更好的方法,但是我想,如果你不想使用条件,你可以使用sigmoid函数,它根据你的条件返回0或1,这个函数就是你的some_operation(x),注意这个函数只对小数位数为2的数字有效,也就是说,输入0.99将返回0,而输入0.999将返回1。2在计算之前,请确保将您的数字向下舍入到最接近的2位小数。
如果有人感兴趣,我将一步一步地经历下面我的思考过程:
如果你想使用一个函数,而不是一个逻辑条件,它必须是连续的,这意味着一些值不符合条件,但如果这些值在一个非常窄的范围内,而你在数字之间的步长大于这个窄范围,它就可以工作。
所以你可以使用一个sigmoid函数。(把它输入wolfram alpha,这样就可以看到每一个变化)

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

向右移动一步,使其以1而不是0为中心。

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

然后你可以通过增加权重来增加函数的斜率。

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

现在斜率非常非常陡,但是如果我们输入x=1,我们仍然得到y = 0.5,所以我们需要把S形曲线向左移一点。

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

现在,如果x〈= 0.99,则y= 0;如果x〉= 1,则y=1。如果要使用更精细的分辨率,则必须调整权重(本例中为10000)和中心点(在这个例子中是0.995)。我刚刚检查了wolfram alpha中的计算,并迭代了什么起作用。如果你只使用2位小数,你可以使用低至4000的权重。
我相信有更好的方法来解决这个问题,但这就是我要解决的问题。

相关问题