- Вопрос или проблема
- Как это длинное определение полезно?
- Ответ или решение
- Как оператор “модуль” (%) в GNU BC полезен при ненулевом масштабе?
- 1. Оператор % и его предназначение
- 2. Полезность остатка при ненулевом масштабе
- 3. Расширяемый масштаб
- 4. Работа с отрицательными числами
- 5. Применение в сложных расчётах
- Заключение
Вопрос или проблема
Это самоотвеченный вопрос, исследование, которое разумно задать для вопроса, идет в части ответа. Пожалуйста, не ставьте минусы, потому что вы считаете, что я недостаточно исследовал для ответа. Спасибо. В любом случае, нет описания (которое я могу найти) этой характеристики % в этом сайте.
При использовании bc
оператор %
утверждается, что вычисляет “остаток”, и да, он работает для целых чисел и когда масштаб равен нулю:
$ bc <<<' scale=0; 27 % 7 '
6
Но он не дает “целый остаток”, если масштаб не ноль:
$ bc <<<' scale=10; 27 % 7 '
.0000000003
Почему (или как) это определение оператора %
модуль полезно?
Оператор %
явно определен в руководстве bc
как [a] :
# Определение внутреннего оператора %:
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) }
Как это длинное определение полезно?
-
Целый остаток
Я покажу как функцию
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
Это не то же самое, что и математическая функция мод. Я решу это ниже.
-
Дробный остаток
Если число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
Это показывает, что остаток стал более универсальным благодаря этому определению.
Теперь это: остаток после значения масштаба.
-
Изменение масштаба
Изменение масштаба требуется, потому что число
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
и оператором%
оба были доказаны как эквивалентные. -
Недоразумение
Будьте осторожны, потому что игра с значением
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 (если вы действительно знаете, что делаете). -
Математический мод
Поскольку мы так глубоко погружаемся в функции мод, нам, вероятно, следует уточнить реальный эффект
%
в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
Знак остатка следует за знаком
делимого
.$ bc <<<'scale=0; n=-13; d=7; n%d; ' -6 $ bc <<<'scale=0; n=-13; d=-7; n%d; ' -6
В то время как правильный математический мод должен давать всегда положительный остаток.
Чтобы получить эту (целочисленную) функцию мод, используйте:
# Модуль с всегда положительным остатком (евклидово деление). define modeuclid(x,div) { if(div!=int(div)){ "ошибка: делитель должен быть целым ";return(0)}; return(x - div*int(x/div)) }
И (тогда) это будет работать:
$ bc <<<"n=7.123456789; d=5; modeuclid(34.123456789,7)" 6.123456789
[a]
expr % expr
Результат выражения – это “остаток”, и он вычисляется следующим образом. Чтобы вычислить a%b, сначала вычисляется a/b до десятичных знаков масштаба. Этот результат используется для вычисления a-(a/b)*b до масштаба максимум scale+scale(b) и scale(a).
Если масштаб установлен в ноль и оба выражения являются целыми, это выражение является функцией целого остатка.
Для кода bc
, следующий за точкой, где было введено это примечание, чтобы работать правильно, определите псевдоним как:
$ alias bc="bc -l "$HOME/.func.bc""
И создайте файл с именем $HOME/.func.bc
, который содержит (по крайней мере):
# Внутреннее определение оператора %:
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) }
# Целая часть числа по направлению к 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 };
# Модуль с всегда положительным остатком (евклидово деление).
define modeuclid(x,div) { if(div!=int(div)){
"ошибка: делитель должен быть целым ";return(0)};
return(x - div*int(x/div)) }
Функцию мод для любого числа (целого или нет) можно определить как:
# Модуль с всегда положительным остатком (евклидово деление).
define modeuclid(x,div) { div=abs(div);return(x - div*floor(x/div)) }
# Округление вниз до целого ниже x (в сторону -бесконечности).
define floor (x) { auto os,y;os=scale;scale=0;
y=x/1;if(y>x){y-=1};scale=os;return(y) };
Это определение абсолютно допустимо и правильно по математическим правилам, однако оно может стать довольно запутанным при попытке применить его в реальных случаях, просто говоря.
Описание BC и его операторов является частью стандарта POSIX. В настоящее время оно доступно здесь:
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html
a % b определяется как a – (a / b) * b и вычисляется до масштаба max(scale + scale(b), scale(a)).
Это имеет место в GNU BC в текущей версии 1.07.1. Для BSD BC является лишь оболочкой для DC, и DC использует “bn” для “%”.
В C-BC это также правда. C-BC, исходный код которого архивирован здесь:
https://github.com/RockBrentwood/CBC
является большим расширением BC, гораздо ближе к C и (в основном) является надмножеством GNU BC и BSD BC.
Ответ или решение
Как оператор “модуль” (%) в GNU BC полезен при ненулевом масштабе?
GNU BC (базовый калькулятор) представляет собой инструмент для выполнения математических расчетов с высокой точностью, поддерживающий операции с числами с плавающей запятой. Одной из интересных особенностей в GNU BC является оператор %
(модуль), который позволяет находить остаток от деления. Однако его поведение может показаться неинтуитивным, особенно при ненулевом масштабе. В этой статье мы раскроем, как и почему операторы и функциональность модуля полезны, даже когда масштаб превышает ноль.
1. Оператор %
и его предназначение
Оператор %
в GNU BC вычисляет остаток от деления с использованием следующей формулы:
[
a \% b = a – (a / b) \times b
]
При этом результат округляется до максимальной точности, заданной как max(scale + scale(b), scale(a))
. Это значит, что в случае работы с вещественными числами и ненулевым масштабом, результаты остаются точными на заданное количество знаков после запятой.
2. Полезность остатка при ненулевом масштабе
Остаток от деления с плавающей точкой: Когда элемент n
имеет больше десятичных знаков, чем d
, оператор %
возвращает остаток, включая результаты после точки. Это позволяет более тонко управлять дробными значениями, которые могут быть важны в ряде вычислительных задач, например, в расчетах, связанных с финансами или статистическими моделями.
$ bc <<< 'scale=10; 27.4321 % 7.3'
0.2321
Этот результат позволяет не просто знать, сколько осталось от целых единиц, но также предоставляет информацию о том, сколько осталось сверх целых единиц, что может быть критически важно.
3. Расширяемый масштаб
Польза от работы с масштабом не ограничивается только остатком возможностей. Если d
(делитель) имеет больше десятичных знаков, чем n
, это требует изменения масштаба для достижения более точного результата:
$ bc <<< 'n=17.123456789; d=1.0001; scale=5; a=n%d; print a'
0.000002789
Здесь видно, что изменение масштаба позволяет добиться большей точности дробей и соответственно обеспечить более точное математическое моделирование.
4. Работа с отрицательными числами
При работе с отрицательными значениями, %
ведет себя согласно правилам округления, которые могут быть полезны в специфических математических задачах. Остаток всегда будет следовать знаку делимого:
$ bc <<< 'scale=0; n=-13; d=7; n%d'
-6
Это свойство может быть критически важным в приложениях, где требуется сохранять знак итогового значения.
5. Применение в сложных расчётах
SQL и другие языки программирования часто требуют от нас выполнения более сложных операций над числами, чем просто целочисленное деление. Использование "%" в таких контекстах подчеркивает его полезность при обработке дробей и в задачах, требующих высоких стандартов точности.
Заключение
Хотя оператор %
в GNU BC не всегда ведет себя так, как ожидается, когда речь идет о ненулевом масштабе, он все же является мощным инструментом для вычислений с высоким уровнем точности. Возможности работы с остатком от деления, включая дробные значения и управление масштабом, расширяют его функционал, делая его незаменимым в расчетах, требующих особого внимания к деталям.
Использование %
в GNU BC позволяет значительно улучшить качество результатов математических операций, и его полезность не следует недооценивать, особенно в научных, инженерных и финансовых приложениях.