GNU BC: ¿Cómo es útil el “módulo” (%) con una escala distinta de 0?

Esta es una pregunta autorepuesta, la investigación que es razonable para hacer una pregunta va en la parte de respuesta. Por favor, no vote negativamente porque crea que no he investigado lo suficiente para obtener una respuesta. Gracias. En cualquier caso, no hay una descripción (que pueda encontrar) de esta característica de bc en este sitio.

Cuando se usa bc, el % calcula el «resto», y sí, funciona para números enteros y cuando la escala es cero :

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

Pero no da el» resto entero «si la escala no es cero:

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

¿Por qué (o cómo) es útil esta definición del % módulo?

Respuesta

El operador % está claramente definido en el 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) } 

Suponiendo que max se haya definido como:

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

¿Qué utilidad tiene esa definición larga?

  1. Resto entero .
    Mostraré ambos y el operador % resultan para demostrar que son equivalentes para algunas de las siguientes operaciones.

    Si los números son enteros, y scale se establece en 0, es la función de resto entero.

    $ 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 

Eso no es lo mismo que la función de modulación matemática. Lo resolveré a continuación.

  1. Resto decimal.
    Si el número n es un número decimal más largo y modificamos la escala, obtenemos:

    $ 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 

    Tenga en cuenta que aquí, se eliminaron los primeros 3 dígitos decimales y el resto es del cuarto 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 

    Eso muestra que el resto se hace más versátil por esa definición.

Ahora es: el resto después de el valor de la escala.

  1. Cambio de escala El cambio de escala es necesario porque el número d (divisor) puede tener más dígitos decimales que n. En ese caso, se necesitan más decimales para tener un resultado más preciso de la divisió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 

    Y, si la escala cambia 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 se puede ver arriba, el valor de la escala cambia para presentar un resultado razonablemente preciso de la división para cualquier valor de n, d y scale.

I» Supondremos que mediante la comparación entre el operador internalmod y % se ha demostrado que ambos son equivalentes.

  1. Confusión . Tenga cuidado porque jugar con el valor de d puede resultar confuso:

    $ bc <<<"n=17.123456789; d=10; scale=3; a=n%d; print a," ",scale(a),"\n"" .003456789 9 

    Y:

    $ bc <<<"n=17.123456789; d=1000; scale=3; a=n%d; print a," ",scale(a),"\n"" .123456789 9 

    Es decir: el valor de d (por encima de 1) modificará el efecto del valor del conjunto de escala.

Probablemente, para valores de d diferentes de 1, debería usar scale = 0 (a menos que realmente sepa lo que está haciendo).

  1. Math mod .
    Ya que estamos Profundizando tanto en las funciones de modificación, probablemente deberíamos aclarar el efecto real de % en bc. El operador % en bc está usando una «división truncada». Uno que se redondea hacia 0. Eso es importante para los valores negativos de n y / o d:

    $ bc <<<"scale=0; n=13; d=7; n%d; " 6 $ bc <<<"scale=0; n=13; d=-7; n%d; " 6 

    El signo del resto sigue al signo de dividend.

    $ bc <<<"scale=0; n=-13; d=7; n%d; " -6 $ bc <<<"scale=0; n=-13; d=-7; n%d; " -6 

    Mientras que un math mod debe dar un resto siempre positivo .

    Para obtener esa función de mod (entero), 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)) } 

    Y (luego) esto funcionará:

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

[a]

expr% expr
El resultado de la expresión es el «resto» y se calcula de la siguiente manera camino. Para calcular a% b, primero se calcula a / b para escalar dígitos.Ese resultado se usa para calcular a- (a / b) * b a la escala del máximo de escala + escala (b) y escala (a).
Si la escala se establece en cero y ambas expresiones son números enteros, esta expresión es la función de resto entero.


Para el código bc que sigue al punto donde esta nota al pie se introdujo para que funcione correctamente, defina un alias como:

$ alias bc="bc -l "$HOME/.func.bc"" 

Y cree un archivo llamado $HOME/.func.bc que contenga ( al 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)) } 

Una función mod para cualquier número (entero o no) puede definirse 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 definición es perfectamente válida y correcta según las reglas matemáticas, sin embargo, puede resultar bastante confuso cuando se trata de aplicarla en casos reales, simplemente diciendo.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *