X86 同时除法并得到余数?

X86 同时除法并得到余数?,x86,modulo,divide,X86,Modulo,Divide,显然,x86(可能还有许多其他指令集)将除法运算的商和余数都放在单独的寄存器中 现在,我们可以相信编译器会优化这样的代码,以便只使用一个调用进行除法: ( x / 6 ) ( x % 6 ) 他们可能是这样的。但是,是否有任何语言(或库,但主要寻找语言)支持同时提供除法和模运算结果?如果是,它们是什么,语法是什么样子的?Python就是这样做的 >>> divmod(9, 4) (2, 1) 这很奇怪,因为Python是一种高级语言 Ruby也是: 11.divmod(3)

显然,x86(可能还有许多其他指令集)将除法运算的商和余数都放在单独的寄存器中

现在,我们可以相信编译器会优化这样的代码,以便只使用一个调用进行除法:

( x / 6 )
( x % 6 )
他们可能是这样的。但是,是否有任何语言(或库,但主要寻找语言)支持同时提供除法和模运算结果?如果是,它们是什么,语法是什么样子的?

Python就是这样做的

>>> divmod(9, 4)
(2, 1)
这很奇怪,因为Python是一种高级语言

Ruby也是:

11.divmod(3) #=> [3, 2]
*编辑*

应该注意的是,这些操作员的目的可能不是尽可能高效地完成工作,更可能是出于正确性/可移植性的原因而存在这些功能

对于那些感兴趣的人,我相信integer divmod的Python实现:

static enum divmod_result
i_divmod(register long x, register long y,
     long *p_xdivy, long *p_xmody)
{
long xdivy, xmody;

if (y == 0) {
    PyErr_SetString(PyExc_ZeroDivisionError,
                    "integer division or modulo by zero");
    return DIVMOD_ERROR;
}
/* (-sys.maxint-1)/-1 is the only overflow case. */
if (y == -1 && UNARY_NEG_WOULD_OVERFLOW(x))
    return DIVMOD_OVERFLOW;
xdivy = x / y;
/* xdiv*y can overflow on platforms where x/y gives floor(x/y)
 * for x and y with differing signs. (This is unusual
 * behaviour, and C99 prohibits it, but it's allowed by C89;
 * for an example of overflow, take x = LONG_MIN, y = 5 or x =
 * LONG_MAX, y = -5.)  However, x - xdivy*y is always
 * representable as a long, since it lies strictly between
 * -abs(y) and abs(y).  We add casts to avoid intermediate
 * overflow.
 */
xmody = (long)(x - (unsigned long)xdivy * y);
/* If the signs of x and y differ, and the remainder is non-0,
 * C89 doesn't define whether xdivy is now the floor or the
 * ceiling of the infinitely precise quotient.  We want the floor,
 * and we have it iff the remainder's sign matches y's.
 */
if (xmody && ((y ^ xmody) < 0) /* i.e. and signs differ */) {
    xmody += y;
    --xdivy;
    assert(xmody && ((y ^ xmody) >= 0));
}
*p_xdivy = xdivy;
*p_xmody = xmody;
return DIVMOD_OK;
}
静态枚举divmod_结果
i_divmod(寄存器长x,寄存器长y,
long*pxdivy,long*pxmody)
{
长春藤,体长;
如果(y==0){
PyErr_设置字符串(PyExc_零位错误,
“整数除零或模零”);
返回DIVMOD_错误;
}
/*(-sys.maxint-1)/-1是唯一的溢出情况*/
如果(y==-1&&一元负会溢出(x))
返回DIVMOD_溢出;
xdivy=x/y;
/*xdiv*y可以在x/y提供地板(x/y)的平台上溢出
*对于具有不同符号的x和y。(这是不寻常的。)
*行为,C99禁止,但C89允许;
*以x=LONG\u MIN、y=5或x为例,说明溢出=
*LONG_MAX,y=-5。)然而,x-xdivy*y总是
*可表示为长,因为它严格位于
*-abs(y)和abs(y)。我们添加铸件以避免中间缺陷
*溢出。
*/
xmody=(长)(x-(无符号长)xdivy*y);
/*如果x和y的符号不同,且余数为非0,
*C89没有定义xdivy现在是地板还是地板
*无限精确商的上限。我们想要地板,
*如果余数的符号与y匹配,我们就得到了它。
*/
如果(xmody&((y^xmody)<0)/*即和符号不同*/){
xmody+=y;
--xdivy;
断言(xmody&((y^xmody)>=0);
}
*p_xdivy=xdivy;
*p_xmody=xmody;
返回DIVMOD_OK;
}
在C#/.NET中,您拥有
Math.DivRem

但据我所知,这并不是什么优化。

该.NET framework具有:

虽然,
DivRem
只是一个包装器,它围绕着如下内容:

int div = x / y;
int mod = x % y;

(我不知道抖动是否可以/确实将这类事情优化为一条指令。)

Common Lisp做到了:

正如Stringer Bell所提到的,有一个
DivRem
,最高可达.NET 3.5

在.NET4.0上

我通过
Math.DivRem
获得的结果(调试;发布=~11000ms)

我使用MyDivRem获得的结果(调试;发布时间=~11000ms)

针对x86的项目


Math.DivRem
用法示例

int mod1;
int div1 = Math.DivRem(4, 2, out mod1);
方法签名

DivRem(Int32, Int32, Int32&) : Int32
DivRem(Int64, Int64, Int64&) : Int64
.NET4.0代码

[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
public static int DivRem(int a, int b, out int result)
{
    result = a % b;
    return (a / b);
}
.NET4.0IL

.custom instance void System.Runtime.TargetedPatchingOptOutAttribute::.ctor(string) = { string('Performance critical to inline across NGen image boundaries') }
.maxstack 8
L_0000: ldarg.2 
L_0001: ldarg.0 
L_0002: ldarg.1 
L_0003: rem 
L_0004: stind.i4 
L_0005: ldarg.0 
L_0006: ldarg.1 
L_0007: div 
L_0008: ret 
C有。这些是否为商和余数生成单独的指令将取决于特定的标准库实现以及编译器和优化设置。从C99开始,对于较大的数字,您还可以使用
lldiv

FWIW,Haskell有两个,后者直接对应于机器指令(根据),而Java中的
divMod
可能没有。

(从1.5开始)类
BigDecimal
具有操作
divideandmainder
返回一个包含2个元素的数组,其中包含除法的结果和反余数

    int result,rest;
    _asm
    {
        xor edx, edx // pone edx a cero; edx = 0
        mov eax, result// eax = 2AF0
        mov ecx, radix // ecx = 4
        div ecx
        mov val, eax
        mov rest, edx
    }
BigDecimal bDecimal = ...
BigDecimal[] result = bDecimal.divideAndRemainder(new BigDecimal(60));

Javadoc:

您的代码片段不是可以以这种方式优化的示例……我刚刚意识到我的代码片段是错误的。更新了。每个人的反应都很好。糟糕的是,当许多答案都是有效答案时,我只能选择一个作为“答案”。是否
divmod
只运行一个操作?这个函数背后的代码是什么?快告诉我。divmod()是Python中的一个内置函数。@BrunoLM我敢打赌大量的[insert favorite beverage]认为,
divmod
只是单独执行这两个操作并打包结果,但没有证据提供。@BrunoLM:VM调用本机函数,我希望它能执行本机div指令。@Russell:hehe;实际上,我的潜在赌注用词不正确!我的意思是,我不认为它试图通过任何低级的“把戏”来提高运营效率,但这只是为开发人员节省一些击键的一种方式:-PIt很惊讶为什么这个答案不被接受-它完全符合要求。有趣的是,在4.8中,
div
没有单独实现模运算:继续并接受了这个答案。我知道这里仍然有很多有效的答案,所以很难说其中一个比其他的更正确,但是C是谈论这些事情的一个好的起点。不要在当前的编译器中使用这一点,尤其是在除以常数时:它不会优化。请参阅:
div(var,10)
编译为实际的函数调用,并且
div
库实现没有除数是常量的信息
10
。所以它不能使用。即使使用运行时变量除数,在x86上也会得到更大的代码大小和非内联函数调用。我肯定看到过一个
div()
函数调用经过优化,可以从单个
div
指令中获得两个结果,其中单独的
/
%
语句有效地运行整个计算两次(我不记得是哪个编译器,但它是一个嵌入式平台)。如果
x
volatile
,您的结果可能会因为完全不同的原因而改变。同样,在为特定用例进行优化之前,请始终使用特定的用例测试特定于实现的行为。这个答案是
.custom instance void System.Runtime.TargetedPatchingOptOutAttribute::.ctor(string) = { string('Performance critical to inline across NGen image boundaries') }
.maxstack 8
L_0000: ldarg.2 
L_0001: ldarg.0 
L_0002: ldarg.1 
L_0003: rem 
L_0004: stind.i4 
L_0005: ldarg.0 
L_0006: ldarg.1 
L_0007: div 
L_0008: ret 
    int result,rest;
    _asm
    {
        xor edx, edx // pone edx a cero; edx = 0
        mov eax, result// eax = 2AF0
        mov ecx, radix // ecx = 4
        div ecx
        mov val, eax
        mov rest, edx
    }
BigDecimal bDecimal = ...
BigDecimal[] result = bDecimal.divideAndRemainder(new BigDecimal(60));