显然,x86(以及可能许多其他指令集)将除法操作的商和余数都放在不同的寄存器中。
现在,我们可以相信编译器会将这样的代码优化为只使用一次除法调用:
( x / 6 )
( x % 6 )
他们很可能会这样做。不过,是否有任何 语言(或库,但主要是语言)支持同时给出除法和取模的结果?如果有,它们是什么,语法是什么样的?
Python可以做到。
>>> divmod(9, 4)
(2, 1)
这很奇怪,因为Python是一种高级语言。
Ruby也是如此:
11.divmod(3) #=> [3, 2]
*** 编辑 ***
需要注意的是,这些运算符的目的可能不是尽可能高效地完成工作,更有可能是为了正确性/可移植性而存在。
对于那些感兴趣的人,我相信这是Python整数divmod
实现的代码:
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
函数只执行一次操作吗?这个函数背后的代码是什么? - BrunoLMdiv
不同,其中余数可以是负余数。因此,即使Python整数是单个“块”(而不是大数),它肯定不能只使用x86 div
。这就是为什么C实现使用/
但不使用%
的原因。 - Peter Cordesreturn rb_assoc_new(num_div(x, y), num_modulo(x, y));
- Simon PerepelitsaMath.DivRem
函数:
http://msdn.microsoft.com/en-us/library/system.math.divrem.aspx。在Java(自1.5版本以来),类BigDecimal
具有操作divideAndRemainder
,返回一个由2个元素组成的数组,其中包含除法的结果和余数。
BigDecimal bDecimal = ...
BigDecimal[] result = bDecimal.divideAndRemainder(new BigDecimal(60));
Java 17 Javadoc: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/math/BigDecimal.html#divideAndRemainder(java.math.BigDecimal)
这是Java 17的Javadoc文档,其中介绍了BigDecimal类的divideAndRemainder方法。该方法可以将一个BigDecimal对象除以另一个BigDecimal对象,并返回商和余数的数组。如果需要在精确计算的情况下进行除法运算,可以使用此方法。详细信息请参见上面提供的链接。.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;
Common Lisp提供以下功能:http://www.lispworks.com/documentation/HyperSpec/Body/f_floorc.htm
DivRem
的函数在.NET 3.5中未进行优化。Math.DivRem
测试的结果(debug; release = ~11000ms)11863
11820
11881
11859
11854
我使用 MyDivRem
得到的结果(调试版; 发布版 = 约11000毫秒)
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
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
}
div
实现:https://dev59.com/Mm855IYBdhLWcg3wZzff - Ciro Santilli OurBigBook.comdiv(var, 10)
编译成实际函数调用,并且div
库实现不知道除数是一个常数的10
。因此,它不能使用乘法逆元。即使是对于运行时变量的除数,你也会得到更大的代码大小和在 x86 上的非内联函数调用。 - Peter Cordesmemcpy
一样),但它们不是,因此它们的优化效果远不如它们本应该有的那么好。因此,最好只使用附近的n / d
和n%d
,并且n
和d
完全相同,以便编译器可以可靠地优化除以常数、常量传播和范围信息。(例如,编译器知道除以10的结果具有比完整的int
范围更小的范围,从而可能允许它稍后进行优化。) - Peter Cordeslibgcc.a
函数,因此您可能甚至从未使用这些函数来节省代码大小。 - Peter Cordesdiv()
函数调用进行优化,以便从单个DIV
指令中获取两个结果,其中分开的/
和%
语句会有效地运行整个计算两次(我不记得是哪个编译器了,不过它是一个嵌入式平台)。如果x
是volatile
的,那么你的结果可能因为完全不同的原因而发生变化。再次强调,在对其进行优化之前,请始终使用特定用例测试与实现相关的行为。 - bta