Разделить и получить остаток одновременно?
По-видимому, x86 (и, вероятно, множество других наборов инструкций) помещают как частное, так и остаток операции деления в отдельные регистры.
Теперь мы, вероятно, можем доверять компиляторам для оптимизации кода, такого как этот, чтобы использовать только один вызов для деления:
( x / 6 )
( x % 6 )
И они, вероятно, делают. И все же, поддерживают ли какие-либо языки (или библиотеки, но в основном ищущие языки) одновременное получение результатов и деления, и модуля? Если да, то каковы они, и как выглядит синтаксис?
10 ответов
С имеет div
а такжеldiv
, Будут ли они генерировать отдельные инструкции для частного и остаточного числа, будет зависеть от вашей конкретной реализации стандартной библиотеки, параметров компилятора и оптимизации. Начиная с C99, у вас также есть lldiv
для больших чисел.
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;
}
В C#/.NET вы получили Math.DivRem
: http://msdn.microsoft.com/en-us/library/system.math.divrem.aspx
Но, согласно этой теме, это не такая уж оптимизация.
В 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
Common Lisp делает: http://www.lispworks.com/documentation/HyperSpec/Body/f_floorc.htm
Как сказал Стрингер Белл, есть DivRem
который не оптимизирован до.NET 3.5.
На.NET 4.0 он использует NGen.
Результаты, которые я получил с Math.DivRem
(отладка; выпуск = ~11000мс)
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
.NET Framework имеет Math.DivRem
:
int mod, div = Math.DivRem(11, 3, out mod);
// mod = 2, div = 3
Хотя, DivRem
это просто обертка вокруг чего-то вроде этого:
int div = x / y;
int mod = x % y;
(Я понятия не имею, может ли джиттер оптимизировать такие вещи в одну инструкцию.)
FWIW, Haskell имеет оба divMod
а также quotRem
последний из которых соответствует непосредственно машинной инструкции (согласно интегральным операторам quot или div), в то время как divMod
может нет.
Это возвращает результат де остатка
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
}
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
}