【问题标题】:Casting unsigned int to unsigned short int with bit operator使用位运算符将 unsigned int 转换为 unsigned short int
【发布时间】:2011-05-19 07:47:58
【问题描述】:

我想通过以下方式将 unsigned int (32bit) A 转换为 unsigned short int (16bit) B:

  • 如果 A
  • 如果 A > 2^16-1 则 B=2^16-1

换句话说,要转换 A,但如果它 > 16 位的最大允许值,则将其设置为最大值。

如何通过位操作或其他非分支方法来实现?

【问题讨论】:

  • 您的意思是“...然后 B=2^16-1”吗?
  • 如果 A > 2^16-1,您的意思是 B=2^16-1 吗?

标签: c casting


【解决方案1】:

找到两个整数中的最小值而不进行分支:

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.3、0.5 和 0.9 秒。真的,这个基准测试没有任何意义,我什至没有检查发出的代码,看看编译器是否足够聪明,可以在某个地方智取我。但我还是喜欢 ruslik 的。

【讨论】:

  • 我在一分钟前正在查看这个,但无法弄清楚这如何应用于不同类型的操作数。
  • 其中有一个&lt;,许多编译器(但不是现代 gcc)会编译到一个分支..
  • @Ross:我在想,取 32 位的最小值,然后转换为 16 位。 @R ..:是的,但希望不是用于分支太慢以至于在这种情况下值得避免的架构的编译器。就此而言,显而易见的方法不会在带有条件指令 (ARM) 的体系结构上分支,如果编译器有任何好处的话。
  • 好的,看起来很明显的解决方案 r = (x
  • @Ross:好吧,在安德森看来。正如你可以从注释中看到的那样,他并不是没有错误的。但这确实表明没有明确的方法可以“预期”在大多数常见实现上更快,或者有人会提交它:-)
【解决方案2】:

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】:

    它适用于无符号值:

    b = -!!(a >> 16) | a;
    

    或者,类似的东西:

    static inline unsigned short int fn(unsigned int a){
        return (-(a >> 16) >> 16) | a;
    };
    

    【讨论】:

    • 我也喜欢你的解决方案。谢谢。
    【解决方案4】:

    首先,在讨论 C 代码时,“非分支方法”这个短语在技术上没有意义;优化器可能会找到从“分支”C代码中删除分支的方法,相反,它完全有权用一个分支替换你聪明的非分支代码,只是为了惹恼你(或者因为一些启发式方法说它会更快)。

    除此之外,简单的表达方式:

    uint16_t b = a > UINT16_MAX ? UINT16_MAX : a;
    

    尽管“有一个分支”,但许多系统上的许多编译器都会将其编译为某种(无分支)条件移动(或可能只是一个饱和)(我刚刚为 ARM 和 Intel 尝试了三种不同的编译器,并且都产生了条件移动)。

    我会使用那个简单易读的表达方式。当且仅当您的编译器不够聪明来优化它(或者您的目标架构没有条件移动),并且如果您有基准数据表明这是您的程序的瓶颈,那么我会(a)找到一个更好的编译器并 (b) 针对您的编译器提交一个错误,然后然后寻找聪明的 hack。

    如果你真的、真的致力于将聪明过半,那么 ruslik 的第二个建议实际上非常漂亮(比通用的最小值/最大值要好得多)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-03
      • 2015-01-20
      • 1970-01-01
      • 1970-01-01
      • 2012-07-11
      • 1970-01-01
      相关资源
      最近更新 更多