有没有办法在不使用任何关系运算符的情况下将大于等于 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.99
和 y=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);
在我的程序中,我在循环中有如下语句。
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.99
和 y=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);