另请参阅另一个旋转问题的此答案的早期版本,其中包含有关asm gcc / clang为x86生成的更多详细信息。

在C和C ++中表达旋转的最容易编译的方法是避免任何未定义的行为,这似乎是John Regehr的实现。 我已经调整它以按类型的宽度旋转(例如假设unsigned long正好是32位宽,尽管C / C ++只保证它至少是那么宽。我试图通过省略那种检查来保持它的可读性 事情)。

#include // for uint32_t

#include // for CHAR_BIT

// #define NDEBUG

#include

static inline uint32_t rotl32 (uint32_t n, unsigned int c)

{

const unsigned int mask = (CHAR_BIT*sizeof(n) - 1); // assumes width is a power of 2.

// assert ( (c<=mask) &&"rotate by type width or more");

c &= mask;

return (n<>( (-c)&mask ));

}

static inline uint32_t rotr32 (uint32_t n, unsigned int c)

{

const unsigned int mask = (CHAR_BIT*sizeof(n) - 1);

// assert ( (c<=mask) &&"rotate by type width or more");

c &= mask;

return (n>>c) | (n<

}

适用于任何无符号整数类型,而不仅仅是unsigned long,因此您可以为其他大小制作版本。

另请参阅具有大量安全检查的C ++ 11模板版本(包括类型宽度为2的幂的unsigned long),例如,某些24位DSP或36位大型机不是这种情况。

我建议只使用模板作为包装器的后端,其名称包含明确的旋转宽度。 整数提升规则意味着unsigned long将执行32或64位旋转,而不是16(取决于unsigned long的宽度)。 即使uint16_t & uint16_t被C ++的整数提升规则提升为signed int,除了在int不宽于ia32intrin.h的平台上。

在x86上,这个版本内联到单个ia32intrin.h(或ia32intrin.h),编译器可以使用它,因为编译器知道x86旋转和移位指令以与C源相同的方式屏蔽移位计数。

编译器支持x86上的UB避免习惯用法,ia32intrin.h和__rolb用于变量计数移位:

clang:在clang3.5之后被识别为变量计数旋转,之前是多次移位+或insn。

gcc:自gcc4.9以来可识别的变量计数旋转,在此之前多次移位+或insn。 gcc5和更高版本也在wikipedia版本中优化了分支和掩码,仅使用ia32intrin.h或__rolb指令进行变量计数。

icc:支持自ICC13或更早版本以来的可变计数旋转。 常量计数旋转使用ia32intrin.h,这比某些CPU(特别是AMD,但也有一些Intel)上的速度慢,占用的字节数多于__rolb,当BMI2不可用于__rorb来保存MOV时。

MSVC:x86-64 CL19:仅识别恒定计数旋转。 (维基百科成语被识别,但分支和AND未被优化)。 在x86(包括x86-64)上使用ia32intrin.h/__rolb内部函数来自__rorb。

ARM的gcc使用ia32intrin.h进行可变计数旋转,但仍然使用单个指令进行实际旋转:ia32intrin.h.因此gcc没有意识到旋转计数本质上是模块化的。 正如ARM文档所说,“ROR的移位长度为ia32intrin.h,超过32与ROR相同,移位长度为__rolb”。 我认为gcc在这里很困惑,因为ARM上的左/右移位使计数饱和,因此移位32或更多将清除寄存器。 (与x86不同,其中移位掩盖计数与旋转相同)。 它可能在识别旋转习语之前决定它需要AND指令,因为非循环移位如何对该目标起作用。

当前的x86编译器仍然使用额外的指令来屏蔽8位和16位旋转的变量计数,这可能与它们不能避免ARM上的AND相同。 这是一个错过的优化,因为性能不依赖于任何x86-64 CPU上的旋转计数。 (出于性能原因,计数掩码是286引入的,因为它迭代地处理了移位,而不是像现代CPU一样处理恒定延迟。)

顺便说一句,更喜欢旋转向右进行可变计数旋转,以避免编译器执行ia32intrin.h以在仅提供旋转右侧的ARM和MIPS等体系结构上实现左旋转。 (这可以通过编译时常量计数来优化。)

有趣的事实:ARM实际上没有专门的移位/旋转指令,它只是MOV,源操作数在ROR模式下通过桶形移位器:ia32intrin.h.因此,旋转可以折叠到EOR指令的寄存器源操作数或一些东西。

确保使用ia32intrin.h的无符号类型和返回值,否则它将不是旋转。 (对于x86目标,gcc会进行算术右移,移位符号位的副本而不是零,当两个移位值一起移位时会导致问题。负有符号整数的右移是C中的实现定义行为。)

此外,确保移位计数是无符号类型,因为带有有符号类型的ia32intrin.h可能是一个补码或符号/幅度,与使用无符号或二进制补码的模块2 ^ n不同。 (参见Regehr博客文章的评论)。 __rolb在我看过的每个编译器上运行良好,每个宽度为__rorb。其他一些类型实际上打败了一些编译器的成语识别,所以不要只使用与__rolw相同的类型。

有些编译器提供了旋转的内在函数,如果可移植版本没有在您要定位的编译器上生成良好的代码,那么它比内联asm好得多。 对于我所知道的任何编译器,没有跨平台的内在函数。 以下是一些x86选项:

英特尔文件ia32intrin.h提供__rolb和__rorb内在函数,右移也是如此。 MSVC需要ia32intrin.h,而gcc需要__rolb. __rorb处理gcc与icc,但是clang似乎没有提供任何地方,除了在MSVC兼容模式下使用ia32intrin.h.并且它为它们发出的asm很糟糕(额外的屏蔽和 CMOV)。

MSVC:ia32intrin.h和__rolb。

gcc和icc(not clang):ia32intrin.h还提供__rolb/__rorb,用于向左/向右旋转8位,__rolw/__rorw(16位),__rold/__rord(32位),__rolq/__rorq(仅限64位,仅限64位) 为64位目标定义)。 对于窄旋转,实现使用__builtin_ia32_rolhi或...qi,但32位和64位旋转使用shift /或(没有针对UB的保护来定义,因为ia32intrin.h中的代码仅需要在x86上使用gcc)。 GNU C似乎没有像__builtin_popcount那样具有任何跨平台__builtin_rotate功能(它扩展到目标平台上的任何最佳功能,即使它不是单个指令)。 大多数时候,你从成语识别中获得了很好的代码。

// For real use, probably use a rotate intrinsic for MSVC, or this idiom for other compilers. This pattern of #ifdefs may be helpful

#if defined(__x86_64__) || defined(__i386__)

#ifdef __MSC_VER

#include

#else

#include // Not just for compilers other than icc

#endif

uint32_t rotl32_x86_intrinsic(rotwidth_t x, unsigned n) {

//return __builtin_ia32_rorhi(x, 7); // 16-bit rotate, GNU C

return _rotl(x, n); // gcc, icc, msvc. Intel-defined.

//return __rold(x, n); // gcc, icc.

// can't find anything for clang

}

#endif

据推测,一些非x86编译器也有内在函数,但是我们不要扩展这个社区维基的答案来包含它们。 (也许在关于内在函数的现有答案中这样做)。

(这个答案的旧版本提出了MSVC特定的内联asm(仅适用于32位x86代码),或[http://www.devx.com/tips/Tip/14043]适用于C版本。评论正在回复 那个。)

内联asm击败了许多优化,特别是MSVC风格,因为它强制输入存储/重新加载。 精心编写的GNU C inline-asm rotate将允许计数成为编译时常量移位计数的立即操作数,但如果要移位的值也是编译时常量,它仍然无法完全优化掉 内联后。[https://gcc.gnu.org/wiki/DontUseInlineAsm。]

c语言中的循环移位函数,C ++中循环移位(旋转)操作的最佳实践相关推荐

  1. java lambda函数_最常用的 Java 8 中的 Lambda 函数(项目中实用笔记)

    最常用的 Java 8 中的 Lambda 函数(项目中实用笔记) 简介 Java 8 中的新特性,虽然现在都出到了Java14版本,不过在日常的开发过程中,8的版本是足够使用了,再说现在的8以上的版 ...

  2. JAVA中的isMirror函数_Swift中的反射Mirror

    Swift中的反射Mirror [TOC] 前言 Mirror是Swift中的反射机制,对于C#和Java开发人员来说,应该很熟悉反射这个概念.反射就是可以动态的获取类型以及成员信息,同时也可以在运行 ...

  3. java中的string函数_java中string.trim()函数的作用实例及源码

    trim()的作用:去掉字符串首尾的空格. public static void main(String arg[]){ String a=" hello world "; Str ...

  4. python中模块和函数_Python中函数和模块的体验与使用

    函数基础 目标 函数的快速体验 函数的基本使用 函数的参数 函数的返回值 函数的嵌套调用 在模块中定义函数 01. 函数的快速体验 1.1 快速体验 所谓函数,就是把 具有独立功能的代码块 组织为一个 ...

  5. usestate中的回调函数_React 中获取数据的 3 种方法:哪种最好?

    译文 | https://cloud.tencent.com/developer/article/1535913 原文 | https://dmitripavlutin.com/ 在执行 I/O 操作 ...

  6. scala中命名参数函数_Scala中带有命名参数的函数

    scala中命名参数函数 具有命名参数的函数 (Functions with named arguments ) A function is Scala can take multiple argum ...

  7. action中写php函数,WordPress中add_action(将函数连接到指定action)

    WordPress中add_action(将函数连接到指定action) 首先说下WordPress中用的钩子,将一个函数,以一个新的动作命名,该函数就是 语法: 1 2 3 add_action($ ...

  8. matlab里inline定义矩阵,Matlab中的inline函数_matlab中inline函数

    Matlab中的inline函数 1.有时为了描述某个数学函数的方便,可以用inline()函数来直接编写该函数,形式相当于M-函数,但无编写一个真正的MATLAB文件,就可以描述出某种数学关系.其调 ...

  9. python中的参数函数_python中函数与参数的简介

    函数 函数就是具有某个具体功能的工具 而使用用函数能提供开发效率,减少代码冗余,提高程序的扩展性. 在Python中,函数有五大要点:分别是def.函数名.函数体.参数.返回值,以及两个英文版符号,分 ...

  10. matlab中的count函数,Excel 中COUNT函数的使用详解,详情介绍

    COUNT在英文上是"统计"的意思,在数学上是"计数"的意思.下面,我们来看看Excel中COUNT函数怎么用吧. 操作方法 01 随便输入数字 在Excel中 ...

最新文章

  1. 服务器端开发经验总结 Linux C语言
  2. 开学季,教你用Python画大学教室座位神分区图!网友直呼“中枪”
  3. Java分布式内存开源实现:Hazelcast
  4. 浅谈 iOS 版本号
  5. Linux command: dos2unix
  6. LeetCode(15):三数之和
  7. 如何读懂 C 语言复杂的声明
  8. [转]C#网络编程(同步传输字符串) - Part.2
  9. 2017sc 膜你赛9 比赛笔记
  10. 训练日志 2019.1.17
  11. Ubuntu 删除dash 中无效的图标
  12. 大数据在企业中的运用
  13. linux 内核块设备驱动,linux之块设备驱动
  14. 国密 GmSSL 版本及安装
  15. 详讯:微软宣布446亿美元收购雅虎
  16. IDEA Tomcat 无法加载mysql驱动
  17. mybatis嵌套查询
  18. kali linux改root密码
  19. 2016年的不正式总结
  20. 为什么要早点进入软件测试行业?现在加入晚了吗?

热门文章

  1. 移动端ajax分页,移动端分页加载 - 花乐天的个人空间 - OSCHINA - 中文开源技术交流社区...
  2. axure 浏览器插件_7款超级实用的谷歌浏览器插件
  3. 第8.18节 Python类中内置析构方法__del__
  4. 2018-12-26 课堂笔记 for循环
  5. 思维导图github地址
  6. Servlet读取xml文件的配置参数
  7. SpringMVC基础学习(二)—开发Handler
  8. [ios开发]锁屏后的相机的方向检查,与图片的自动旋转
  9. ASP.NET MVC 3—一切的开始MvcHandler、MvcHttpHandler
  10. sshd启动报错解决:sshd re-exec requires execution with an absolute path