英文:
a bitwise expression that returns 1 when mask is all zeros and x when mask is all ones
问题
让我们假设我们有一个名为 mask
的变量,它可以是 0
(全零)或 -1
(全一)。
我有另一个变量 x
,假设它是 42
。
可能的位运算表达式是什么,可以实现以下结果:
- 当
mask == 0
时,结果为1
- 当
mask == -1
时,结果为x
我试图探索消除以下 if
语句的方法:
unsigned x = 3;
unsigned some_number = 42;
// ~~~
if (some_number % x == 0)
{
some_number /= x;
}
关于我正在解决的特定问题中的变量类型(请看下文),所有数字都是无符号整数,例如 unsigned
。
编辑 2(错误):我犯了个错误,忘记了 mask
可以是 0
(全零)或 -1
(全一),而我之前说过是 1
(只有最低有效位为一)。请接受我的道歉,因为我浪费了您宝贵的时间。
编辑 1:
总的来说,重点不是使用 if-else
结构(包括条件运算符(?:
))。我正在寻找类似于在无分支实践中使用的位操作方式。这个问题不是关于在这种情况下是否更高效,或者答案是否可以被视为良好实践的问题。只是为了探索位操作的世界。
以下是我的尝试:
// 检查 some_number 是否能被 x 整除
unsigned mask = ((some_number % x) - 1) >> 31;
unsigned denom = // ??
some_number /= denom;
我正在使用这个来回答项目欧拉的第三个问题,这里是代码:
size_t largest_prime_factor(size_t number)
{
if (number < 2) return 0;
if (number == 2) return 2;
while ((number & 1) == 0)
{
number >>= 1;
}
size_t lpf = 2;
size_t i = 3;
size_t max_iter = sqrt(number);
while (i < max_iter)
{
size_t mask = ((number % i) - 1) >> 31;
lpf = (i & mask) | (lpf & ~mask);
number /= x; // mask = 0 => x=1; mask = 1 => x=i
i += 2;
}
return lpf;
}
注意:如果可能的话,请建议一个更好的标题。
英文:
Let's say we have a mask
variable that can either be 0
(all zoers) or -1
(all ones).
I have another variable x
which is let's say 42
.
What (possibly) is the bitwise expression that results in:
1
whenmask == 0
x
whenmask == -1
what I'm trying to do is explore ways to eliminate the following if
statement
unsigned x = 3;
unsigned some_number = 42;
// ~~~
if (some_number % x == 0)
{
some_number /= x;
}
Regarding the type of variables in the particular problem that I'm working on (look below), all of the numbers are unsigned integers for example unsigned
.
EDIT 2 (MISTAKE): I made a mistake and forgot that mask is either 0
(all zeros) or, -1
(all ones) which I previously said 1
(only whose least significant bit is one). Please accept my apologies as I wasted your precious time.
EDIT 1:
Overall the point is not to use if-else
constructs (including the conditional operator (?:
)). Instead I'm looking for bit manipulations similar to those which is used in branchless practices. This question isn't whether it is going to be more performant or not in this case, or whether the answer could be considered a good practice or not. It's just to explore the world of bit manipulation.
Here is my try
// Check whether some_number is divisible by x
unsigned mask = ((some_number % x) - 1) >> 31;
unsigned deonm = // ??
some_number =/ denom
I'm using this to answer project Euler's third question and here's the code
size_t largest_prime_factor(size_t number)
{
if (number < 2) return 0;
if (number == 2) return 2;
while ((number & 1) == 0)
{
number >>= 1;
}
size_t lpf = 2;
size_t i = 3;
size_t max_iter = sqrt(number);
while (i < max_iter)
{
size_t mask = ((number % i) - 1) >> 31;
lpf = (i & mask) | (lpf & ~mask);
number /= X; // mask = 0 => X=1; mask = 1 => X=i
i += 2;
}
return lpf;
}
NOTE: Please, if possible, suggest a better title.
答案1
得分: 3
在您的特定情况下,您可能不希望避免分支:除法可能很昂贵,而正确预测的分支可能很便宜。如果分支不经常执行(例如,暴力因数分解),则结果将比始终除以计算出的除数要快。
如果您只想要一个无分支的解决方案:
(x - 1) * mask + 1
可以工作,但由于乘法操作,不一定会更快。或者,如果mask
是无符号的,您可以使用((x - 1) & -mask) + 1
,这可能稍微更快。
您还可以稍微并行化这个过程以减少依赖链:(x & -mask) + !mask
。
英文:
In your particular case, you may not want to avoid the branch: divisions can be expensive, while a correctly predicted branch can be cheap. If the branch is not taken frequently (e.g. brute-force factoring), the result will be faster than trying to always divide by a computed divisor.
If you just want a branchless solution:
(x - 1) * mask + 1
works, but won't necessarily be faster due to the multiply. Alternatively, if mask
is unsigned, you could use ((x - 1) & -mask) + 1
which is probably slightly faster.
You can also parallelize this a bit to reduce the dependency chain: (x & -mask) + !mask
.
答案2
得分: 2
对于 `mask` 可以是 `0` 或 `1` 的情况
```c
1 - mask // 或 !mask => 当 mask 为 0 时为 1
+
x * mask // => 当 mask 为 1 时为 x
英文:
For the case where mask
can be either 0
or 1
1 - mask // or !mask => 1 when mask is 0
+ // or |
x * mask // => x when mask is 1
答案3
得分: 1
我会假设掩码(我将其称为m
)、x
和结果(我将其称为r
)都是相同大小的无符号整数类型。我将称此类型为TYPE
。
对于大多数位来说,很容易:ri = xi & mi。
然而,最不显著的位(r0)不同。以下显示了根据m0和x0的值所需的r0的值:
r0 | m0 = 0 | m0 = 1 |
---|---|---|
x0 = 0 | 1 | 0 |
x0 = 1 | 1 | 1 |
因此,我们得到 r0 = ~m0 | x0。
现在,我们只需要将它们组合起来。
我们想要将第一个规则应用于除了最后一位之外的所有位。这可以通过使用一个允许除了最后一位之外的所有位通过的掩码来完成(... & ( ~(TYPE)1 ))。
我们想要仅针对最后一位应用第二个规则。这可以通过使用一个仅允许最后一位通过的掩码来完成(... & 1)。
全部放在一起:
r = ( x & m & ( ~(TYPE)1 ) ) | ( ( ~m | x ) & 1 );
现在我们有一个只使用位操作的解决方案(和一个可能可以通过使用正确的数字文字后缀来避免的强制转换)。
英文:
I'm going to assume the mask (which I'll call m
), x
, and the result (which I'll call r
) are unsigned integer types of the same size. I shall call that type TYPE
.
For most of the bits, it's easy: r<sub>i</sub> = x<sub>i</sub> & m<sub>i</sub>
The least significant bit (r<sub>0</sub>) is different, though. The following shows the desired value for r<sub>0</sub> based on the values of m<sub>0</sub> and x<sub>0</sub>:
r<sub>0</sub> | m<sub>0</sub> = 0 | m<sub>0</sub> = 1 |
---|---|---|
x<sub>0</sub> = 0 | 1 | 0 |
x<sub>0</sub> = 1 | 1 | 1 |
So we get r<sub>0</sub> = ~m<sub>0</sub> | x<sub>0</sub>
Now, we just need to combine them.
We want to apply the first rule to all the bits but the last. This can be done by using a mask that lets through all bits but the last (... & ( ~(TYPE)1 )
).
We want to apply the second rule for just the last bit. This can be done by using a mask that lets through only the last bit (... & 1
).
All together:
r = ( x & m & ( ~(TYPE)1 ) ) | ( ( ~m | x ) & 1 );
And now we have a solution with just bit operations (and a cast which can maybe be avoided by using the correct numeric literal suffix).
答案4
得分: 1
((x ^ 1) & mask) ^ 1
英文:
With xor, you can get a shorter solution:
((x ^ 1) & mask) ^ 1
答案5
得分: 0
一个位与操作几乎可以产生所需的输出:
- 如果您的无符号掩码全为1
-1U
,则mask & x == x
- 如果您的无符号掩码全为零
0U
,则mask & x == 0
我们可以通过另一个操作来补充看到:
- 如果您的无符号掩码全为1
-1U
,则mask + 1 == 0
- 如果您的无符号掩码全为零
0U
,则mask + 1 == 1
通过总结这两个表达式 mask + 1 + (mask & x)
,您可以获得您想要的结果:
- 当
mask == 0U
时,得到1
- 当
mask == -1U
时,得到x
英文:
A bit and operation does almost produce the desired output:
- if your unsigned mask is all ones
-1U
thenmask & x == x
- if your unsigned mask is all zeros
0U
thenmask & x == 0
We see that we can complement with another operation:
- if your unsigned mask is all ones
-1U
thenmask + 1 == 0
- if your unsigned mask is all zeros
0U
thenmask + 1 == 1
By summing up the two expressions mask + 1 + (mask & x)
you get what you want:
1
whenmask == 0U
x
whenmask == -1U
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论