Если масштаб отличается от нуля, вычисления с%, например, 3% 2 и 46% 4, имеют тенденцию выводить 0. Как алгоритм разработан с масштабом, отличным от 0?
bc
scale=10
print 4%3 // output 0
Если масштаб отличается от нуля, вычисления с%, например, 3% 2 и 46% 4, имеют тенденцию выводить 0. Как алгоритм разработан с масштабом, отличным от 0?
bc
scale=10
print 4%3 // output 0
В руководстве по команде сказано, что BC вычисляет по модулю:
Результатом выражения является "остаток", который вычисляется следующим образом. Чтобы вычислить% b, сначала вычисляется a/b для масштабирования цифр. Этот результат используется для вычисления a - (a /b) * b по шкале максимума scale+scale (b) и scale (a). Если масштаб установлен на ноль, и оба выражения являются целыми числами, это выражение является функцией целочисленного остатка.
scale
, целочисленное деление не происходит.
Попробуйте это в BC:
bc
scale = 0
print 5/2
scale = 5
print 5/2
вы должны получить:
2 << Integer Division
2.50000 << NOT integer division!
Теперь давайте вставим эти цифры так, как это делает BC. В руководстве сказано, что для вычисления используется a-(a/b)* b . Давайте добавим два наших результата, один из которых получен в результате целочисленного деления, а другой - со 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, чтобы модуль работал правильно.
Кажется, проблема возникает из-за дизайна BC и того, как он обрабатывает числа со «шкалой». Чтобы модуль работал правильно, нам нужно целочисленное деление.
Существуют и другие , более продвинутые инструменты , которые бесплатны и открыты для этой цели, и я рекомендую вам их использовать.
Пользователь user272970 отлично ответил. Вот твик к нему:
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, без необходимости каждый раз вручную определять эти функции.
scale
ps 1000 может быть излишней в большинстве случаев
Я решил это так:
определить int(x) {oldscale = scale; масштаб = 0; х = х /1; Шкала = oldscale; возврат (х); }
определить мод (х, у) {oldscale = масштаб; масштаб = 1000; x = x - y * int (x/y); Шкала = oldscale; возврат (х); }
НТН
Как сказал другой ответ, это результат определения 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
Оператор %
четко определен в руководстве по bc
как [a] :
# Internal % operator definition:
define internalmod(n,d,s) { auto r,oldscale;
oldscale=scale; r=n/d;
s=max(s+scale(d),scale(n));
scale=s; r = n-(r)*d;
scale=oldscale; return(r) }
Предполагая, что max
был определен как:
define max(x,y){ if(x>y){return(x)};return(y) }
1. Целочисленный остаток.
Я буду использовать как функцию internalmod
и оператор %
чтобы показать, что они эквивалентны для некоторых операций ниже
Если числа целые и масштаб установлен на 0, это целочисленная функция остатка.
$ bc <<<'n=17; d=3; scale=0; a=internalmod(n,d,scale); b=n%d; print a," ",b,"\n"'
2 2
$ bc <<<'n=17; d=6; scale=0; a=internalmod(n,d,scale); b=n%d; print a," ",b,"\n"'
5 5
Это не то же самое, что математическая мод-функция. Я решу это ниже.
2. Десятичный остаток.
Если число n
является более длинным десятичным числом, и мы изменяем масштаб, мы получаем:
$ bc <<<'n=17.123456789;d=1; scale=0 ;a=internalmod(n,d,scale);b=n%d;print a," ",b,"\n"'
.123456789 .123456789
$ bc <<<'n=17.123456789;d=1; scale=3 ;a=internalmod(n,d,scale);b=n%d;print a," ",b,"\n"'
.000456789 .000456789
Обратите внимание, что здесь первые 3 десятичных знака были удалены, а остаток от четвертого десятичного знака.
$ bc <<<'n=17.123456789;d=1; scale=7 ;a=internalmod(n,d,scale);b=n%d;print a," ",b,"\n"'
.000000089 .000000089
Это показывает, что остальное сделано более универсальным по этому определению.
3. Изменение масштаба Изменение масштаба требуется, потому что число d
(делитель) может иметь больше десятичных цифр, чем n
. В этом случае требуется больше десятичных знаков, чтобы получить более точный результат от деления:
$ bc <<<'n=17.123456789; d=1.00000000001; scale=0; a=internalmod(n,d,scale);
b=n%d; print a," ",scale(a)," -- ", b," ",scale(b),"\n"'
.12345678883 11 -- .12345678883 11
И, если масштаб меняется:
$ bc <<<'n=17.123456789; d=1.00000000001; scale=5; a=internalmod(n,d,scale);
b=n%d; print a," ",scale(a)," -- ", b," ",scale(b),"\n"'
.0000067888287655 16 -- .0000067888287655 16
Как можно видеть выше, значение масштаба увеличивается, чтобы представить достаточно точный результат деления для любого значения n
, d
и scale
.
Я предполагаю, что при сравнении internalmod
и оператора %
оба оказались эквивалентными.
4. Путаница
Будьте осторожны, потому что игра со значением d
может сбить с толку:
$ bc <<<'n=17.123456789; d=10; scale=3; a=n%d; print a," ",scale(a),"\n"'
.003456789 9
А также:
$ bc <<<'n=17.123456789; d=1000; scale=3; a=n%d; print a," ",scale(a),"\n"'
.123456789 9
То есть значение d
(выше 1) изменит эффект от значения установленной шкалы.
Вероятно, для значений d
отличных от 1, вы должны использовать scale = 0 (если вы действительно не знаете, что делаете).
5. Математический мод.
Так как мы глубоко погружаемся в мод-функции, нам, вероятно, следует выяснить реальный эффект %
в bc
. Оператор %
в bc использует "усеченное деление". Тот, который округляется до 0
. Это важно для отрицательных значений n
и / или d
:
$ bc <<<'scale=0; n=13; d=7; n%d; '
6
$ bc <<<'scale=0; n=13; d=-7; n%d; '
6
Знак остатка следует за знаком dividend
.
$ bc <<<'scale=0; n=-13; d=7; n%d; '
-6
$ bc <<<'scale=0; n=-13; d=-7; n%d; '
-6
Хотя правильный математический мод должен давать всегда положительный остаток.
Чтобы получить эту (целочисленную) функцию мода, используйте:
# Module with an always positive remainder (euclid division).
define modeuclid(x,div) { if(div!=int(div)){
"error: divisor should be an integer ";return(0)};
return(x - div*int(x/div)) }
И это будет работать:
$ bc <<<"n=7.123456789; d=5; modeuclid(34.123456789,7)"
6.123456789
[А]
expr% expr
Результатом выражения является "остаток", который вычисляется следующим образом. Чтобы вычислить% b, сначала вычисляется a/b для масштабирования цифр. Этот результат используется для вычисления a-(a/b) * b по шкале максимума scale+scale(b) и scale(a).
Если масштаб установлен на ноль, и оба выражения являются целыми числами, это выражение является функцией целочисленного остатка.
Для кода bc
, следующего за точкой, в которой эта сноска была введена для правильной работы, определите псевдоним как:
$ alias bc='bc -l "$HOME/.func.bc"'
И создайте файл с именем $HOME/.func.bc
который содержит (как минимум):
# Internal % operator definition:
define internalmod(n,d,s) { auto r,oldscale;
oldscale=scale; r=n/d;
s=max(s+scale(d),scale(n));
scale=s; r = n-(r)*d;
scale=oldscale; return(r) }
# Max function
define max(x,y){ if(x>y){return(x)};return(y) }
# Integer part of a number toward 0: -1.99 -> -1, 0.99 -> 0
define int(x) { auto os;os=scale;scale=0;
x=sgn(x)*abs(x)/1;scale=os;return(x) }
define sgn (x) { if (x<0){x=-1};if(x>0){x=1};return(x) };
define abs (x) { if (x<0) x=-x; return x };
# Module with an always positive remainder (euclid division).
define modeuclid(x,div) { if(div!=int(div)){
"error: divisor should be an integer ";return(0)};
return(x - div*int(x/div)) }
Мод-функция для любого числа (целое или нет) может быть определена как:
# Module with an always positive remainder (Euclid division).
define modeuclid(x,div) { div=abs(div);return(x - div*floor(x/div)) }
# Round down to integer below x (toward -inf).
define floor (x) { auto os,y;os=scale;scale=0;
y=x/1;if(y>x){y-=1};scale=os;return(y) };
Это определение совершенно правильно и правильно по математическим правилам, однако, оно может стать довольно запутанным, когда вы пытаетесь применить его в реальных случаях, просто говоря.