我想以下列方式将unsigned int(32bit)A转换为unsigned short int(16bit)B:
换句话说,如果是< 16bit的最大允许值,将其设置为最大值。
如何通过位操作或其他非分支方法实现这一目标?
答案 0 :(得分:5)
它适用于无符号值:
b = -!!(a >> 16) | a;
或类似的东西:
static inline unsigned short int fn(unsigned int a){
return (-(a >> 16) >> 16) | a;
};
答案 1 :(得分:2)
找到最少两个没有分支的整数:
http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
在一些罕见的分支机器上 是非常昂贵,没有条件 移动指令存在,上面 表达式可能比 明显的方法,r =(x
只是为了解决问题,这是一个脑死亡的基准。我试图“随机”获得50/50的大小值组合:
#include <iostream>
#include <stdint.h>
int main() {
uint32_t total = 0;
uint32_t n = 27465;
for (int i = 0; i < 1000*1000*500; ++i) {
n *= 30029; // worst PRNG in the world
uint32_t a = n & 0x1ffff;
#ifdef EMPTY
uint16_t b = a; // gives the wrong total, of course.
#endif
#ifdef NORMAL
uint16_t b = (a > 0xffff) ? 0xffff : a;
#endif
#ifdef RUSLIK
uint16_t b = (-(a >> 16) >> 16) | a;
#endif
#ifdef BITHACK
uint16_t b = a ^ ((0xffff ^ a) & -(0xffff < a));
#endif
total += b;
}
std::cout << total << "\n";
}
在我的编译器(带有-O3的cygwin上的gcc 4.3.4)中,NORMAL
获胜,然后是RUSLIK
,然后是BITHACK
,分别比“{0}”快0.3到0.5和0.9秒空循环。真的这个基准没什么意义,我甚至没有检查发出的代码,看看编译器的智能是否足以智胜我。但无论如何我喜欢ruslik。
答案 2 :(得分:0)
1)在CPU上具有本机执行此类转换的内在函数。
2)你可能不喜欢这样,但是:
c = a >> 16; /* previously declared as a short */
/* Saturate 'c' with 1s if there are any 1s, by first propagating
1s rightward, then leftward. */
c |= c >> 8;
c |= c >> 4;
c |= c >> 2;
c |= c >> 1;
c |= c << 1;
c |= c << 2;
c |= c << 4;
c |= c << 8;
b = a | c; /* implicit truncation */
答案 3 :(得分:0)
首先,在讨论C代码时,短语“非分支方法”在技术上没有意义;优化器可能会找到从“分支”C代码中删除分支的方法,相反,完全在其权限内用一个分支替换你聪明的非分支代码只是为了惹恼你(或者因为一些启发式的说它会更快)。
除此之外,简单的表达方式:
uint16_t b = a > UINT16_MAX ? UINT16_MAX : a;
尽管“有一个分支”,但很多系统上的许多编译器都会被编译成某种(无分支)条件移动(或者可能只是一个饱和的)(我刚刚为ARM和Intel尝试了三种不同的编译器,以及所有都产生了条件性的移动。)
我会使用那个简单易读的表达方式。当且仅当您的编译器不够智能以优化它(或者您的目标架构没有条件移动),并且如果您有基准数据显示这是您的程序的瓶颈,那么我会(a)找到一个更好的编译器和(b)针对您的编译器提交错误,然后 寻找聪明的黑客。
如果你真的,真的致力于过于聪明一半,那么 ruslik 的第二个建议实际上非常漂亮(比通用的最小/最大更好)。