GNU BC: Como o “módulo” (%) com escala diferente de 0 é útil?

Esta é uma pergunta auto-respondida , a pesquisa que é razoável fazer para uma pergunta vai na parte das respostas. Por favor, não faça votos negativos porque você acredita que eu não pesquisei o suficiente para uma resposta. Obrigado. Em qualquer caso, não há nenhuma descrição (que eu possa encontrar) desta característica de bc neste site.

Ao usar bc, o % calcula o “resto” e, sim, funciona para números inteiros e quando a escala é zero :

$ bc <<<" scale=0; 27 % 7 " 6 

Mas falha em fornecer o” resto inteiro “se a escala não for zero:

$ bc <<<" scale=10; 27 % 7 " .0000000003 

Por que (ou como) esta definição do % módulo é útil?

Resposta

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) } 

Supondo que max tenha sido definido como:

define max(x,y){ if(x>y){return(x)};return(y) } 

Como essa definição longa é útil?

  1. Resto de número inteiro .
    Mostrarei ambos os internalmod função e os resultados do operador % para provar que eles são equivalentes para algumas das próximas operações.

    Se os números forem inteiros, e a escala é 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 do mod matemático. Vou resolver isso abaixo.

  1. Restante decimal.
    Se o número n for um número decimal mais longo e modificarmos a escala, obtemos:

    $ 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 

    Observe que aqui, os três primeiros dígitos decimais foram removidos e o restante dado vem 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 torna-se mais versátil por essa definição.

Agora é: o resto após o valor da escala.

  1. 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 e:

    $ 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 muda para apresentar um resultado razoavelmente preciso da divisão para qualquer valor de n, d e scale.

I” Presumimos que, pela comparação entre o internalmod e o % operador, ambos foram comprovados como equivalentes.

  1. 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 d diferentes de 1, você deve usar scale = 0 (a menos que realmente saiba o que está fazendo).

  1. Mod matemático .
    Já que somos dando um mergulho profundo 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 gira em direção a 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 resto 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 

    Enquanto um correto matemática mod deve dar um resto sempre positivo .

    Para obter essa função mod (número 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 (então) isso funcionará:

    $ bc <<<"n=7.123456789; d=5; modeuclid(34.123456789,7)" 6.123456789 

[a]

expr% expr
O resultado da expressão é o “resto” e é calculado da seguinte forma maneira. Para calcular a% b, primeiro a / b é calculado para dimensionar dígitos.Esse resultado é usado para calcular a- (a / b) * b para a escala do máximo de escala + escala (b) e escala (a).
Se a escala for definida como zero e ambas as expressões forem inteiros, esta expressão é a função de resto inteiro.


Para o código bc que segue o ponto onde esta nota de rodapé foi introduzido 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 pelas regras matemáticas, no entanto, pode se tornar bastante confusa ao tentar aplicá-la em casos reais, basta dizer.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *