
Si la escala es distinta de cero, los cálculos con %, como 3%2 y 46%4, tienden a generar 0. ¿Cómo se diseña el algoritmo con una escala distinta de 0?
bc
scale=10
print 4%3 // output 0
Respuesta1
Elmanual de comandodice esto sobre cómo BC calcula el módulo:
El resultado de la expresión es el "resto" y se calcula de la siguiente manera. Para calcular a%b, primeroa/bse calcula a escala de dígitos. Ese resultado se utiliza para calculara - ( a/b ) * ba 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 de enteros.
EDITAR: Miré el código fuente de GNU BC y descubrí que el operador mod extiende el operador de división. En otras palabras, el módulo se calcula como un subproducto de la división. Se basa en la división de enteros para calcular el módulo. Cuando
scale
se establece, sin embargo, no se produce la división de enteros.
Pruebe esto en BC:
bc
scale = 0
print 5/2
scale = 5
print 5/2
deberías obtener:
2 << Integer Division
2.50000 << NOT integer division!
Ahora conectemos estas cifras como lo hace BC. El manual dice que usaa-(a/b)*bcalcular. Conectemos nuestros dos resultados, el que resulta de la división de enteros y el que tiene un valor scale
distinto de 0.
a - ( a/b ) * b
5 - ( 2 ) * 2 = 1 << CORRECT!
5 - ( 2.5 ) * 2 = 0 << VERY WRONG!
Sin división de números enteros:
a - ( a/b ) * b == a - ( a ) == 0
Es por eso que la escala debe establecerse en 0 para que el módulo funcione correctamente.
El problema parece surgir del diseño de BC y de cómo maneja los números con una "escala". Para que el módulo funcione correctamentenecesitamos división entera.
Hayotromuchoherramientas más avanzadasque son gratuitos y de código abierto para este propósito, y te recomiendo que los uses.
Respuesta2
La respuesta del usuario272970 es genial. Aquí hay un ajuste:
define int(x) { auto oldscale; oldscale=scale; scale=0; x=x/1; scale=oldscale; return( x ); }
define fmod(x,y) { auto oldscale; oldscale=scale; scale=1000; x = x - y * int(x/y); scale=oldscale; return( x ); }
Esto (usando auto oldscale
) hace que oldscale
la función sea local. Sin eso, configurar oldscale
desde int()
fmod() sobrescribirá el oldscale
que está intentando guardarse fmod()
, dejando scale
el valor 1000 en lugar de lo que tenía antes de llamar fmod()
.
Agregué estas funciones ~/.bcrc
y configuré la BC_ENV_ARGS
variable de entorno en ~/.bcrc
. Eso cargará estas funciones cada vez que ejecute bc. Así que ahora puedo ejecutar fmod(x,y)
en cualquier momento que esté en bc sin tener que definir manualmente esas funciones cada vez.
ps scale
de 1000 puede ser excesivo en la mayoría de los casos
Respuesta3
Lo resolví de esta manera:
entero
definir int(x) { escala antigua=escala; escala=0; x=x/1; escala=escala antigua; retorno(x); }
módulo
define mod(x,y) { escala antigua=escala; escala=1000; x = x - y * int(x/y); escala=escala antigua; retorno(x); }
HT
Respuesta4
Como ha dicho otra respuesta, es el resultado de definir a%b
como (a-(a/b)*b)
, evaluado en el momento actual scale
. Esto significa que si desea que actúe como un módulo entero, debe usarlo con scale=0
.
Sin embargo, no estoy de acuerdo con que sea "incorrecto". Es una herramienta potencialmente útil, especialmente para evaluar errores.
scale=5
1/3
> .33333
1%3
> .00001
¿Qué estamos perdiendo si lo representamos 7/13
como decimal de 4 dígitos .5384
?
scale=4
7/13
> .5384
7%13
> .0008
Aparentemente 0.0008/13
.
Y, por último, como no insiste en utilizar números enteros, se puede utilizar para extraer una parte de un decimal.
scale=1
123.456/1
> 123.4
123.456%1
> .056