7

Если масштаб отличается от нуля, вычисления с%, например, 3% 2 и 46% 4, имеют тенденцию выводить 0. Как алгоритм разработан с масштабом, отличным от 0?

bc
scale=10
print 4%3   // output 0

5 ответов5

10

В руководстве по команде сказано, что BC вычисляет по модулю:

Результатом выражения является "остаток", который вычисляется следующим образом. Чтобы вычислить% b, сначала вычисляется a/b для масштабирования цифр. Этот результат используется для вычисления a - (a /b) * b по шкале максимума scale+scale (b) и scale (a). Если масштаб установлен на ноль, и оба выражения являются целыми числами, это выражение является функцией целочисленного остатка.


РЕДАКТИРОВАТЬ: Я посмотрел на исходный код для GNU BC и обнаружил, что оператор мода расширяет оператор деления. Другими словами, модуль вычисляется как побочный продукт деления. Для вычисления по модулю используется целочисленное деление. Когда установлен 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 и того, как он обрабатывает числа со «шкалой». Чтобы модуль работал правильно, нам нужно целочисленное деление.

Существуют и другие , более продвинутые инструменты , которые бесплатны и открыты для этой цели, и я рекомендую вам их использовать.

3

Пользователь 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 может быть излишней в большинстве случаев

3

Я решил это так:

целое число

определить int(x) {oldscale = scale; масштаб = 0; х = х /1; Шкала = oldscale; возврат (х); }

по модулю

определить мод (х, у) {oldscale = масштаб; масштаб = 1000; x = x - y * int (x/y); Шкала = oldscale; возврат (х); }

НТН

0

Как сказал другой ответ, это результат определения 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
0

Оператор % четко определен в руководстве по 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) };

Это определение совершенно правильно и правильно по математическим правилам, однако, оно может стать довольно запутанным, когда вы пытаетесь применить его в реальных случаях, просто говоря.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .