所以今天我想问一个相当存在的问题,我觉得好像大多数程序员跳过并接受一些有效的东西,而不是真正问“它是如何工作”的问题。问题很简单:> =运算符如何编译成机器代码,该机器代码是什么样的?在最底部,它必须大于测试,混合“相等”测试。但这实际上是如何实现的呢?考虑它似乎相当矛盾,因为在最底层不可能有一个>或==测试。还需要别的东西。我想知道这是什么。
计算机如何测试相等性和大于基本水平?
答案 0 :(得分:2)
确实没有>
或==
测试。相反,汇编程序中的最低级别比较由binary subtraction起作用。
在x86上,整数比较的操作码是CMP
。它实际上是一条指令来统治它们。如何在80386 Programmer's reference manual:
CMP
从第一个操作数中减去第二个操作数,但与SUB
指令不同,它不存储结果;只有标志被改变。
CMP
通常与条件跳转和SETcc
指令一起使用。 (有关提供的有符号和无符号标志测试列表,请参阅附录D.)如果将大于一个字节的操作数与立即数字进行比较,则首先对字节值进行符号扩展。
基本上,CMP A, B
(在Intel operand ordering中)会计算A - B
,然后会丢弃结果。但是,在x86 ALU中,算术运算根据操作结果在CPU的标志寄存器内设置条件标志。与算术运算相关的标志是
Bit Name Function
0 CF Carry Flag -- Set on high-order bit carry or borrow; cleared
otherwise.
6 ZF Zero Flag -- Set if result is zero; cleared otherwise.
7 SF Sign Flag -- Set equal to high-order bit of result (0 is
positive, 1 if negative).
11 OF Overflow Flag -- Set if result is too large a positive number
or too small a negative number (excluding sign-bit) to fit in
destination operand; cleared otherwise.
例如,如果计算结果为零,则设置Zero Flag ZF
。 CMP A, B
执行A - B
并丢弃结果。减法的结果是0 iff A == B
。因此,ZF
仅在操作数相等时设置,否则清除。
如果无符号减法会导致borrow,则会设置进位标记CF
,如果A - B
< 0
将是A
}和B
被视为 unsigned
数字和A < B
。
只要设置了结果的MSB位,就会设置符号标志。这意味着作为带符号数的结果在2的补码中被认为是负数。但是,如果您考虑8位减法01111111
(127) - 10000000
( - 128),则结果为11111111
,其解释为8位有符号2&#39;补码是-1
,即使127 - (-128)
应为255
。发生有符号整数溢出单独的符号标志并不能单独告知哪些有符号数量更大 - OF
溢出标志指示在先前的算术运算中是否发生了有符号溢出。
现在,根据使用它的位置,Byte Set on Condition SETcc
或Jump if Condition is Met Jcc
指令用于解码标志并对其进行操作。如果布尔值用于设置变量,那么聪明的编译器将使用SETcc
; Jcc
if
更适合else
... >=
。
现在,int a, b;
bool r1, r2;
unsigned int c, d;
r1 = a >= b; // signed
r2 = c >= d; // unsigned
有两种选择:要么是签名比较,要么是无符号比较。
r2
在英特尔汇编中,无符号不等式的条件名称使用上方和下方;签名平等的条件使用单词更高和更少。因此,对于(CF=0)
,编译器可以决定使用 Set on Above或Equal ,即SETAE
,如果r1
,则将目标字节设置为1。对于SETGE
,结果将由(SF=OF)
解码 - 在大或等上设置字节,这意味着#include <stdbool.h>
bool gte_unsigned(unsigned int a, unsigned int b) {
return a >= b;
}
- 即,被解释为2的补码的结果为正溢出,或溢出发生时为负。
最后一个例子:
cmp edi, esi
setae al
ret
在x86-64 Linux上生成的优化代码是:
bool gte_signed(int a, int b) {
return a >= b;
}
同样用于签名比较
cmp edi, esi
setge al
ret
生成的程序集是
...
events: <?= $response ?>.filter(function(x) { return x.status === "1";}),
...
答案 1 :(得分:1)
这是一个简单的C函数:
bool lt_or_eq(int a, int b)
{
return (a <= b);
}
在x86-64上,GCC将其编译为:
.file "lt_or_eq.c"
.text
.globl lt_or_eq
.type lt_or_eq, @function
lt_or_eq:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %eax
cmpl -8(%rbp), %eax
setle %al
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size lt_or_eq, .-lt_or_eq
重要的部分是cmpl -8(%rbp), %eax; setle %al;
序列。基本上,它使用http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/webob.html指令以数字方式比较两个参数,并根据该比较设置零标志和进位标志的状态。然后,它使用cmp
来决定是否将%al 寄存器设置为0
或1
,具体取决于这些标志的状态。调用者从%al 寄存器中获取返回值。
答案 2 :(得分:0)
首先,计算机需要确定数据的类型。在像C这样的语言中,这将是在编译时,python将在运行时调度到不同类型的特定测试。假设我们来自一个编译语言,并且我们知道我们比较的值是整数,那么编译器将确保值在寄存器中,然后发出:
ffmpeg -i input.mp4 -vf fps=1/10 out%03d.jpg
减去寄存器,然后检查零/ undflow。这些指令在CPU上运行。 (我在这里假设是ARM式的,有很多变化)。