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

0

Esta é uma pergunta auto-respondida , a pesquisa que é razoável pedir uma pergunta vai na parte de resposta, por favor não downvote porque você acredita que eu não pesquisei bastante para uma resposta. Obrigado. Em qualquer caso, não há descrição (que eu possa encontrar) dessa característica de bc neste site.

Ao usar bc , o operador % é reivindicado para calcular o "resto" e, sim, funciona para números inteiros e quando a escala é zero :

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

Mas não dá o "resto inteiro" se a escala não for zero:

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

Por que (ou como) essa definição do % modulo é útil?

    
por Isaac 28.10.2018 / 03:02

1 resposta

0

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. Número inteiro restante .
    Mostrarei os resultados do operador internalmod e % para provar que eles são equivalentes para algumas das próximas operações.

    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.

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

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

  1. Alteração 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 muda 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.

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

  1. 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 (então) 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.

    
por 28.10.2018 / 03:02

Tags