GNU BC: «по модулю»% со шкалой, отличной от 0

12971
KronoS

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

bc scale=10 print 4%3 // output 0 
7
Для тех, кто просто хочет что-то, что работает: `define mod (x, base) ` Hello World 9 лет назад 0

5 ответов на вопрос

10
jweede

В руководстве по команде сказано, что 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 . Давайте добавим два наших результата: один с целочисленным делением, а другой с scale0.

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 и того, как он обрабатывает числа с «масштабом». Чтобы модуль работал правильно, нам нужно целочисленное деление .

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

Я также получаю те же результаты. tj111 14 лет назад 0
Ваша шкала равна 0, а не «кроме нуля». Пожалуйста, установите его, например, с помощью «scale = 10», а затем попробуйте «3% 2». 14 лет назад 0
что означает «шкала + шкала (б)»? Должно ли это быть «масштаб (а) + масштаб (б)»? Или, возможно, масштаб (а) +1 и масштаб (б) +1 для хороших верхних границ. Я не могу понять его цель, иначе. 14 лет назад 0
Цитата точно из: http://www.gnu.org/software/bc/manual/html_chapter/bc_3.html#SEC10 14 лет назад 0
когда он говорит «масштаб» без скобок, это относится к глобальной переменной «масштаб». jweede 14 лет назад 2
Это все еще не имеет смысла. Почему это не «шкала + шкала (б) и шкала + шкала (а)»? 14 лет назад 0
Что автор подразумевает под "a- (a / b) * b"? 0? 14 лет назад 0
Порядок в расчете: @ 1-й. k = a / b @ 2nd. а - к * б 14 лет назад 0
Дополнительные пояснения были добавлены. jweede 14 лет назад 0
Я не уверен, почему это должно быть `scale + scale (b)`, поскольку `scale` для a / b обычно должно быть равно нулю, чтобы дать значимый вывод. jweede 14 лет назад 1
Конечно, дизайн делает BC более эффективным для небольшой «отладки», хотя и не уверен, насколько. Упомянутые вами другие инструменты могут лучше справляться с этим типом, но за счет эффективности. +1 14 лет назад 1
3
user272970

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

целое число

определить int (x)

по модулю

определить мод (х, у)

НТН

3
Juan

Пользователь 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 без необходимости вручную каждый раз определять эти функции.

ps scale1000 может быть излишним в большинстве случаев

0
zebediah49

Как сказал другой ответ, это результат определения a%bкак (a-(a/b)*b), оцененный в текущем scale. Это означает, что если вы хотите, чтобы он действовал как целочисленный модуль, вам нужно использовать его с scale=0.

Однако я не согласен с тем, что это «неправильно». Это потенциально полезный инструмент, особенно для оценки ошибок.

scale=5 1/3 > .33333 1%3 > .00001 

Что мы теряем, если представляем 7/134-значное десятичное число .5384?

scale=4 7/13 > .5384 7%13 > .0008 

Видимо 0.0008/13.

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

scale=1 123.456/1 > 123.4 123.456%1 > .056 
0
Isaac

%Оператор четко определено в 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, был определен как:

define max(x,y){ if(x>y);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. %Оператор Ьс использует «усечение деления». Тот, который округляет в сторону 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(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);if(x>0);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);scale=os;return(y) }; 

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

Похожие вопросы