O operador %
é claramente definido no bc
manual como [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) }
Assumindo que max
foi definido como:
define max(x,y){ if(x>y){return(x)};return(y) }
Como essa longa definição é útil?
1. Inteiro restante .
Usarei a função internalmod
e o operador %
para mostrar que são equivalentes para algumas operações abaixo
Se os números forem inteiros e a escala for definida como 0, é a função de resto inteiro.
$ 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
Isso não é o mesmo que a função matemática mod. Eu vou resolver isso abaixo.
2. Decimal restante.
Se o número n
for um número decimal mais longo e modificarmos a escala, obteremos:
$ 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
Note que aqui os 3 primeiros dígitos decimais foram removidos e o restante dado é do quarto dígito decimal.
$ bc <<<'n=17.123456789;d=1; scale=7 ;a=internalmod(n,d,scale);b=n%d;print a," ",b,"\n"'
.000000089 .000000089
Isso mostra que o restante é mais versátil por essa definição.
3. Mudança de escala
A mudança de escala é necessária porque o número d
(divisor) pode ter mais dígitos decimais do que n
. Nesse caso, mais decimais são necessários para ter um resultado mais preciso da divisão:
$ 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
E, se a escala mudar:
$ 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
Como pode ser visto acima, o valor da escala se expande para apresentar um resultado razoavelmente preciso da divisão para qualquer valor de n
, d
e scale
.
Assumirei que, pela comparação entre o operador internalmod
e o %
, ambos provaram ser equivalentes.
4. Confusão .
Tenha cuidado, porque brincar com o valor de d
pode se tornar confuso:
$ bc <<<'n=17.123456789; d=10; scale=3; a=n%d; print a," ",scale(a),"\n"'
.003456789 9
E:
$ bc <<<'n=17.123456789; d=1000; scale=3; a=n%d; print a," ",scale(a),"\n"'
.123456789 9
Ou seja: o valor de d
(acima de 1) modificará o efeito do valor do conjunto de escala.
Provavelmente, para valores de d
diferentes de 1, você deve usar a escala = 0 (a menos que você realmente saiba o que está fazendo).
5. Mod matemática .
Como estamos nos aprofundando nas funções mod, provavelmente devemos esclarecer o efeito real de %
em bc
. O operador %
em bc está usando uma "divisão de truncamento". Um que arredonda para 0
. Isso é importante para valores negativos de n
e / ou d
:
$ bc <<<'scale=0; n=13; d=7; n%d; '
6
$ bc <<<'scale=0; n=13; d=-7; n%d; '
6
O sinal do restante segue o sinal do dividend
.
$ bc <<<'scale=0; n=-13; d=7; n%d; '
-6
$ bc <<<'scale=0; n=-13; d=-7; n%d; '
-6
Embora um mod matemático correto deva dar um resto sempre positivo .
Para obter essa função mod (inteiro), use:
# 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)) }
E isso funcionará:
$ bc <<<"n=7.123456789; d=5; modeuclid(34.123456789,7)"
6.123456789
[a]
expr % expr
The result of the expression is the "remainder" and it is computed in the following way. To compute a%b, first a/b is computed to scale digits. That result is used to compute a-(a/b)*b to the scale of the maximum of scale+scale(b) and scale(a).
If scale is set to zero and both expressions are integers this expression is the integer remainder function.
Para o código bc
que segue o ponto em que esta nota de rodapé foi introduzida para funcionar corretamente, defina um alias como:
$ alias bc='bc -l "$HOME/.func.bc"'
E crie um arquivo chamado $HOME/.func.bc
que contenha (pelo menos):
# 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)) }
Uma função mod para qualquer número (inteiro ou não) pode ser definida como:
# 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) };
Esta definição é perfeitamente válida e correta por regras matemáticas, no entanto, pode se tornar bastante confuso ao tentar aplicá-la em casos reais, apenas dizendo.