
Если масштаб отличен от нуля, вычисления с %, например 3%2 и 46%4, как правило, дают 0. Как разработан алгоритм с масштабом, отличным от 0?
bc
scale=10
print 4%3 // output 0
решение1
Theруководство по командованиюВот как BC вычисляет модуль:
Результатом выражения является «остаток», и он вычисляется следующим образом. Чтобы вычислить a%b, сначалаа/бвычисляется для масштабирования цифр. Этот результат используется для вычисленияа - ( а/б ) * бк масштабу максимума масштаба+масштаб(b) и масштаба(a).Если масштаб установлен на ноль и оба выражения являются целыми числами, то это выражение является функцией остатка от целочисленного числа.
РЕДАКТИРОВАТЬ: Я посмотрел исходный код GNU BC и обнаружил, что оператор mod расширяет оператор деления. Другими словами, остаток вычисляется как побочный продукт деления. Он полагается на целочисленное деление для вычисления остатка.
scale
Однако, когда установлено , целочисленное деление не происходит.
Попробуйте это в Британской Колумбии:
bc
scale = 0
print 5/2
scale = 5
print 5/2
вы должны получить:
2 << Integer Division
2.50000 << NOT integer division!
Теперь давайте подставим эти цифры так, как это делает BC. В руководстве говорится, что он используета-(а/б)*бдля вычисления. Давайте подставим два наших результата: один, полученный в результате целочисленного деления, и один, в котором scale
не равно 0.
a - ( a/b ) * b
5 - ( 2 ) * 2 = 1 << CORRECT!
5 - ( 2.5 ) * 2 = 0 << VERY WRONG!
Без целочисленного деления:
a - ( a/b ) * b == a - ( a ) == 0
Вот почему масштаб должен быть установлен на 0, чтобы modulo работал правильно.
Проблема, похоже, возникает из-за конструкции BC и того, как он обрабатывает числа со «масштабом». Для того, чтобы modulo работал правильнонам нужно целочисленное деление.
Естьдругоймногоболее продвинутые инструментыкоторые бесплатны и имеют открытый исходный код для этой цели, и я рекомендую вам их использовать.
решение2
Ответ пользователя 272970 отличный. Вот его подстройка:
define int(x) { auto oldscale; oldscale=scale; scale=0; x=x/1; scale=oldscale; return( x ); }
define fmod(x,y) { auto oldscale; oldscale=scale; scale=1000; x = x - y * int(x/y); scale=oldscale; return( x ); }
Это (использование auto oldscale
) делает oldscale
локальным для функции. Без этого установка oldscale
из int()
fmod() перезапишет то oldscale
, что пытается быть сохранено в fmod()
, оставив scale
установленным 1000 вместо того, что было до вызова fmod()
.
Я добавил эти функции ~/.bcrc
и установил BC_ENV_ARGS
переменную окружения в ~/.bcrc
. Это загрузит эти функции каждый раз, когда вы запускаете bc. Так что теперь я могу просто запускать их fmod(x,y)
в любое время, когда я в bc, без необходимости вручную определять эти функции каждый раз.
ps scale
1000 может быть излишним в большинстве случаев
решение3
Я решил это следующим образом:
целое число
определить int(x) { старыймасштаб = масштаб; масштаб = 0; x = x/1; масштаб = старыймасштаб; return( x ); }
по модулю
определить mod(x,y) { старыймасштаб=масштаб; масштаб=1000; x = x - y * int(x/y); масштаб=старыймасштаб; return( x ); }
НТН
решение4
Как сказали другие ответы, это результат определения a%b
как (a-(a/b)*b)
, оцененный в текущем scale
. Это означает, что если вы хотите, чтобы он действовал как целочисленный модуль, вам нужно использовать его с scale=0
.
Однако я не согласен, что это "неправильно". Это потенциально полезный инструмент, особенно для оценки ошибок.
scale=5
1/3
> .33333
1%3
> .00001
Что мы теряем, если представим 7/13
в виде 4-значной десятичной дроби .5384
?
scale=4
7/13
> .5384
7%13
> .0008
Видимо 0.0008/13
.
И, наконец, поскольку он не требует использования целых чисел, его можно использовать для извлечения части десятичной дроби.
scale=1
123.456/1
> 123.4
123.456%1
> .056