同时划分并获得剩余?

时间:2010-10-09 00:03:19

标签: x86 modulo divide

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

现在,我们可以信任编译器优化这样的代码,只使用一次调用来划分:

( x / 6 )
( x % 6 )

他们可能会这样做。仍然,做任何语言(或库,但主要是寻找语言)支持同时给出除法和模数结果?如果是这样,它们是什么,语法是什么样的?

10 个答案:

答案 0 :(得分:46)

C有div and ldiv。这些是否为商和余数生成单独的指令将取决于您的特定标准库实现以及编译器和优化设置。从C99开始,对于更大的数字,您还有lldiv

答案 1 :(得分:30)

Python确实。

>>> divmod(9, 4)
(2, 1)

这很奇怪,因为Python是如此高水平的语言。

Ruby也是如此:

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

*编辑*

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

对于那些感兴趣的人,我相信整数divmod的Python实现的this is the code

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;
}

答案 2 :(得分:10)

在C#/ .NET中你有Math.DivRemhttp://msdn.microsoft.com/en-us/library/system.math.divrem.aspx

但根据this thread,这不是一个优化。

答案 3 :(得分:3)

答案 4 :(得分:3)

正如Stringer Bell所提到的那样DivRem is not optimized达到.NET 3.5。

在.NET 4.0上it uses NGen

我使用Math.DivRem得到的结果(debug; release = ~11000ms)

11863
11820
11881
11859
11854

结果我得到了MyDivRem(debug; release = ~11000ms)

29177
29214
29472
29277
29196

针对x86的项目。


Math.DivRem用法示例

int mod1;
int div1 = Math.DivRem(4, 2, out mod1);

方法签名

DivRem(Int32, Int32, Int32&) : Int32
DivRem(Int64, Int64, Int64&) : Int64

.NET 4.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);
}

.NET 4.0 IL

.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 

MSDN Reference

答案 5 :(得分:2)

.NET框架有Math.DivRem

int mod, div = Math.DivRem(11, 3, out mod);
// mod = 2, div = 3

虽然,DivRem只是这样的包装:

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

(我不知道抖动是否可以/确实将这种事物优化为单个指令。)

答案 6 :(得分:1)

FWIW,Haskell有divModquotRem,后者直接对应机器指令(根据Integral operators quot vs. div),而divMod可能不对。

答案 7 :(得分:0)

    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
    }

答案 8 :(得分:0)

这会返回结果和余数

        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
    }

答案 9 :(得分:0)

在Java中,类BigDecimal的操作divideAndRemainder返回一个包含结果的2个元素的数组和除法的剩余部分。

BigDecimal bDecimal = ...
BigDecimal[] result = bDecimal.divideAndRemainder(new BigDecimal(60));

Javadoc:https://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html#divideAndRemainder(java.math.BigDecimal)