为什么编译器除以 2 时会右移 31 位?

2024-02-03

我已经反汇编了编译器生成的代码,我发现它生成了以下指令序列:

mov     eax, edx
shr     eax, 1Fh
add     eax, edx
sar     eax, 1  

这段代码的目的是什么?


我知道

sar     eax, 1

除以 2,但是什么

shr     eax, 1Fh

做?这是否意味着EAX如果左边位为 0 或 1,则为 0 或 1?

这对我来说看起来很奇怪!有人可以解释一下吗?


快速回答您的问题——什么是shr eax, 1Fh- 是它用于隔离最上面的位eax。如果把十六进制转换一下可能会更容易理解1Fh到小数31。现在,你发现你正在转变eax31 号之后。eax是一个 32 位值,将其位右移 31 将隔离最高位,这样eax将包含 0 或 1,具体取决于位 31 的原始值(假设我们从 0 开始对位进行编号)。

这是隔离的常见技巧sign bit。当一个值在补码机上被解释为有符号整数时,最高位是符号位。如果值为负则设置 (== 1),否则清除 (== 0)。当然,如果该值被解释为无符号整数,则最高位只是用于存储其值的另一位,因此最高位具有任意值。


逐行进行反汇编,代码的作用如下:

mov     eax, edx

显然,输入是在EDX。该指令复制值EDX into EAX。这允许后续代码操作中的值EAX不丢失原来的(在EDX).

shr     eax, 1Fh

Shift EAX右移 31 位,从而隔离最高位。假设输入值是有符号整数,这将是符号位。EAX如果原始值为负,则现在将包含 1,否则将包含 0。

add     eax, edx

添加原始值(EDX)到我们的临时值EAX。如果原始值为负数,则会加 1。否则就加0。

sar     eax, 1

Shift EAX就差 1 个位置。这里的区别在于这是一个算术右移,而SHR is a logical右移。逻辑移位用 0 填充新暴露的位。算术移位将最高位(符号位)复制到新公开的位。


综合起来,这是将有符号整数值除以 2 的标准习惯用法,以确保负值正确舍入.

当你划分一个unsigned值除 2,只需要简单的位移即可。因此:

unsigned Foo(unsigned value)
{
    return (value / 2);
}

相当于:

shr  eax, 1

但是当除以有符号值时,必须处理符号位。你可以使用sar eax, 1实现有符号整数除以 2,但这会导致结果值向负无穷大舍入。请注意,这与DIV/IDIV指令,总是向零舍入。如果您想模拟向零舍入行为,则需要一些特殊处理,这正是您所拥有的代码所做的。事实上,当您编译以下函数时,GCC、Clang、MSVC 以及可能所有其他编译器都将准确生成此代码:

int Foo(int value)
{
    return (value / 2);
}

这是一个very老把戏。迈克尔·阿布拉什 (Michael Abrash) 在他的汇编语言之禅,发表circa 1990. (这是相关部分 http://www.jagregory.com/abrash-zen-of-asm/#signed-division-with-sar在那之前,这肯定是汇编语言专家们的常识。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么编译器除以 2 时会右移 31 位? 的相关文章

  • 汇编8086监听键盘中断

    我有与此完全相同的问题 边画边听键盘 https stackoverflow com questions 13970325 8086 listen to keyboard while drawing 但第一个答案 接受的答案 只听键盘一次
  • 在 x86 ASM 中测试零通常哪个更快:“TEST EAX, EAX”与“TEST AL, AL”?

    测试 AL 中的字节是否为零 非零通常哪个更快 TEST EAX EAX TEST AL AL 假设之前有一个 MOVZX EAX BYTE PTR ESP 4 指令加载了一个带有零扩展的字节参数到 EAX 的其余部分 防止了我已经知道的组
  • 如何分析Android应用程序的电池使用情况并对其进行优化?

    我想分析我的应用程序的电池使用情况 我的意思是应用程序的各个部分 例如 广播接收器 监听器 服务等 使用多少电池 我需要一个详细的列表 从列表中 我想优化电池的使用 方法与使用内存分析器类似 http android developers
  • Nasm 打印到下一行

    我用 nasm Assembly 编写了以下程序 section text global start start Input variables mov edx inLen mov ecx inMsg mov ebx 1 mov eax 4
  • 68HC11计算sin(x)的汇编代码

    68HC11 使用泰勒级数或查找表计算正弦值的汇编代码是什么 显示值只能是整数 查找表如何工作 在这种情况下 如何使用它来实现泰勒级数 http en wikipedia org wiki Taylor series 如果您正在寻找浮点解决
  • “rep stos”x86 汇编指令序列有什么作用?

    我最近偶然发现了以下汇编指令序列 rep stos dword ptr edi For ecx重复 存储内容eax到哪里edi指向 递增或递减edi 取决于方向标志 每次 4 个字节 通常 这用于memset型操作 通常 该指令简单地写成r
  • 从 XML 构建树结构的速度很慢

    我正在将 XML 文档解析为我自己的结构 但对于大型输入来说构建它非常慢 是否有更好的方法来做到这一点 public static DomTree
  • 弹出 x86 堆栈以访问函数 arg 时出现分段错误

    我正在尝试链接 x86 程序集和 C 我的C程序 extern int plus 10 int include
  • 我有*很多*源文件要添加到 git 存储库,如何使其快速

    我在看here https git scm com docs git fast import寻找更快地将批量文件导入 git 存储库的灵感 但不确定是不是这样 基本上情况是 我有超过 1 亿个文件想要提交到 git 存储库 我已将它们分解为
  • 从 exe 文件中获取汇编级代码?

    我当时正在做linux汇编编程 在过去的几天里我已经转而学习windows汇编编程 我在用ml作为我的汇编器和golink作为我的链接器 我有我的汇编代码并已获得我的exe从中 现在我需要取回它的十六进制 xff xab x55等等 在li
  • 如何在 Debian 上编译 DOS 程序?

    在我的汇编语言课程中 我们使用 DPMI 编写 DOS 程序 不幸的是 我无法一直使用 32 位 Windows 机器 我在我使用的几乎每台计算机上都安装了 Debian 虚拟机 我已经安装了 DOSBox 和 DOSEMU 有什么办法可以
  • “mov (%ebx,%eax,4),%eax”如何工作? [复制]

    这个问题在这里已经有答案了 一直在从事装配作业 并且在很大程度上我对装配非常了解 或者至少对于这项任务来说足够好 但这个 mov 的声明让我很困惑 如果有人能解释这个 mov 语句如何操作寄存器值 我将非常感激 mov ebx eax 4
  • UV 展开运行时优化

    我正在尝试在运行时创建 UV 我使用 BOX 类型 UV 类似于 3ds max 中的 BOX UVW 并基于面方向进行计算 我知道将其创建为运行时不是一个好的选择 但我别无选择 它是在计算后保存的 所以我做了一次 但我花了 40 秒处理
  • 使用 MIPS 从 Big Endian 到 Little Endian 无需逻辑运算?

    我正在使用 MIPS QtSpim 将 32 位字从 Big Endian 转换为 Little Endian 我下面显示的内容已检查且正确 不过我想知道还有什么其他方法可以让我进行转换 我虽然只使用了旋转和移位 但如果没有逻辑运算 我就无
  • 为什么如果内存组织为字,则程序计数器加 1;如果内存组织为字节,则程序计数器加 2?

    如果在计算机中一条指令是 16 位 并且如果存储器被组织为 16 位字 则通过在当前指令的地址中加 1 来计算下一条指令的地址 如果内存是按字节组织的 可以单独寻址 那么我们需要在当前指令地址上加二 得到顺序执行的下一条指令的地址 为什么会
  • 确定向量中是否存在元素的最有效方法

    我有几种算法取决于确定元素是否存在于向量中的效率 在我看来 这 in 这相当于is element 应该是最有效的 因为它只返回一个布尔值 在测试了几种方法之后 令我惊讶的是 这些方法是迄今为止效率最低的 以下是我的分析 随着向量大小的增加
  • X86 预取优化:“计算 goto”线程代码

    我有一个相当重要的问题 我的计算图有循环和多个 计算路径 我没有制作一个调度程序循环 其中每个顶点将被一一调用 而是将所有预先分配的 框架对象 放置在堆中 代码 数据 这有点类似于线程代码 甚至更好 CPS 只是在堆中跳转 执行代码 每个代
  • 如何编译GCC生成的asm?

    我正在玩一些汇编代码 有些事情困扰着我 我编译这个 include
  • 为什么 clang 使用 -O0 生成低效的 asm(对于这个简单的浮点和)?

    我正在 llvm clang Apple LLVM 版本 8 0 0 clang 800 0 42 1 上反汇编此代码 int main float a 0 151234 float b 0 2 float c a b printf f c
  • 适用于多应用项目的 Grunt 和 requirejs 优化器

    我在让 Grunt 对具有以下结构的项目执行 requirejs 优化时遇到问题 static js apps app js dash js news js many more app files build collections lib

随机推荐