有没有办法在没有分支的情况下将整数值限制在某个范围内?

时间:2010-05-19 18:49:48

标签: c++ branch

出于好奇。如果我有类似的东西:

if(x < 0)
    x = 0;
if(x > some_maximum)
    x = some_maximum;

return x;

有没有办法不分支?这是c ++。

附录:我的意思是装配中没有分支指令。这是一个MIPS架构。

9 个答案:

答案 0 :(得分:28)

有些技巧可以找到两个数字的最小值或最大值,因此您可以使用它们来查找min(max(x, 0), some_maximum)。来自here

y ^ ((x ^ y) & -(x < y)); // min(x, y)
x ^ ((x ^ y) & -(x < y)); // max(x, y)

正如消息来源所述,尽管有分支

,但以正常方式执行它可能会更快

答案 1 :(得分:13)

这将取决于编译器和处理器,但如果使用?:,它可以转换为不使用分支的条件移动(至少在基于Intel的处理器上)。

x = x < 0 ? 0 : x; x = x > max ? max : x;

这可以使用CMOV指令(参见http://www.intel.com/software/products/documentation/vlin/mergedprojects/analyzer_ec/mergedprojects/reference_olh/mergedProjects/instructions/instruct32_hh/vc35.htm),其目的是避免分支(以及分支预测惩罚)。

修改:您可能会对此thread感兴趣。基准测试表明,条件移动只会在不可预测的分支上提供速度增益,而高度可预测的分支(例如长时间循环的分支)更喜欢标准方法。

答案 2 :(得分:3)

使用三元运算符:)

return x < 0 ? 0 : x > some_maximum ? : some_maximum : x;

答案 3 :(得分:2)

取决于您的架构。至少对于ARM,编译器可能会发出有条件执行的指令,并且生成的机器代码不会包含分支。我想不出一个在C程序中明确表达的好方法。

答案 4 :(得分:2)

在C ++ 17中,您可以使用std::clamp

  

在标题Comparator<Object> yourComparator = ...; BiPredicate<Object,Object> bip = (d1,d2) -> comp.compare(d1,d2) == 0; List<Object> data = ...; List<Object> result = data.stream().pairMap(bip).collect(Collectors.toList());

中定义
<algorithm>
     
      
  1. 如果v比较小于lo,则返回lo;否则,如果比较   小于v,返回hi;否则返回v。使用operator <来   比较这些值。
  2.   
  3. 与(1)相同,但是使用comp来比较值。
  4.   

答案 5 :(得分:1)

如果可以限制为2(非包含)的幂,那么只需使用

int newx = x & ((highest power of 2) - 1)

不太确定要处理(如果x&lt; 0情况)或通用(x&lt; n case)

答案 6 :(得分:1)

对于像这样的未来问题,位黑客页面可能很有用:http://graphics.stanford.edu/~seander/bithacks.html

由于已经发布了min和max的bithack,这里有一个不同的:

// CHAR_BIT is number of bits per byte.
// sign = 1 if x < 0, sign = 0 otherwise (according to the page above)
int sign = (int)((unsigned int)((int)x) >> (sizeof(int) * CHAR_BIT - 1));

int y = (1-sign)*x; // if x < 0, then y = 0, else y = x.

// Depending on arch, the below _might_ cause a branch.
// (on x64 it does not cause a branch, not sure about MIPS)

int z = !(y/some_maximum); // if 0 <= y < some_maximum, z = 1, else z = 0

int ret = z*y + (1-z)*some_maximum; // if z =1, then ret = y; else ret = some_maximum.

return ret;

我刚试了一下,它适用于我所拥有的几个测试用例。

这是我的计算机(intel arch)的汇编代码,它没有显示分支。

int cap(int x)
{
00F013A0  push        ebp  
00F013A1  mov         ebp,esp 
00F013A3  sub         esp,0FCh 
00F013A9  push        ebx  
00F013AA  push        esi  
00F013AB  push        edi  
00F013AC  lea         edi,[ebp-0FCh] 
00F013B2  mov         ecx,3Fh 
00F013B7  mov         eax,0CCCCCCCCh 
00F013BC  rep stos    dword ptr es:[edi] 

    int some_maximum = 100;

00F013BE  mov         dword ptr [some_maximum],64h 

    // CHAR_BIT is number of bits per byte. 
    // sign = 1 if x < 0, sign = 0 otherwise (according to the page above) 
    int sign = (int)((unsigned int)((int)x) >> (sizeof(int) * CHAR_BIT - 1)); 

00F013C5  mov         eax,dword ptr [x] 
00F013C8  shr         eax,1Fh 
00F013CB  mov         dword ptr [sign],eax 

    int y = (1-sign)*x; // if x < 0, then y = 0, else y = x. 

00F013CE  mov         eax,1 
00F013D3  sub         eax,dword ptr [sign] 
00F013D6  imul        eax,dword ptr [x] 
00F013DA  mov         dword ptr [y],eax 

    // Depending on arch, the below _might_ cause a branch. 
    // (on x64 it does not cause a branch, not sure about MIPS) 

    int z = !(y/some_maximum); // if 0 <= y < some_maximum, z = 1, else z = 0 

00F013DD  mov         eax,dword ptr [y] 
00F013E0  cdq              
00F013E1  idiv        eax,dword ptr [some_maximum] 
00F013E4  neg         eax  
00F013E6  sbb         eax,eax 
00F013E8  add         eax,1 
00F013EB  mov         dword ptr [z],eax 

    int ret = z*y + (1-z)*some_maximum; // if z =1, then ret = y; else ret = some_maximum. 

00F013EE  mov         eax,dword ptr [z] 
00F013F1  imul        eax,dword ptr [y] 
00F013F5  mov         ecx,1 
00F013FA  sub         ecx,dword ptr [z] 
00F013FD  imul        ecx,dword ptr [some_maximum] 
00F01401  add         eax,ecx 
00F01403  mov         dword ptr [ret],eax 

    return ret; 

00F01406  mov         eax,dword ptr [ret] 
}
00F01409  pop         edi  
00F0140A  pop         esi  
00F0140B  pop         ebx  
00F0140C  mov         esp,ebp 
00F0140E  pop         ebp  
00F0140F  ret              

答案 7 :(得分:0)

x = min(max(x,0),100);

分支隐藏在具有普通名称的函数内。

建议创建clip_by模板。

答案 8 :(得分:0)

x =   ((int)(x > some_maximum)) * some_maximum 
    + ((int)(x > 0 && x <= some_maximum)) * x;