pseint - divisible c++
¿Comprobar si un doble es divisible por otro doble en C? (7)
¿Cómo puedo verificar si una doble x es divisible por otra doble y en C? Con los enteros solo usaría el módulo, pero ¿cuál sería la forma correcta / mejor de hacerlo con dobles?
Sé que los números de punto flotante llevan consigo la imprecisión, pero obtengo el doble de la entrada estándar. Tal vez no debería escanearlo como un doble al instante, sino como dos enteros, pero ¿a dónde iría a partir de entonces?
¿Cómo puedo verificar si una doble x es divisible por otra doble y en C? Con los enteros solo usaría el módulo, pero ¿cuál sería la forma correcta / mejor de hacerlo con dobles?
Incluiría y enlazaría a la biblioteca de matemáticas:
#include <math.h>
Entonces llamaría a la función de módulo de punto flotante fmod :
if (fmod(5.0, 2.5) == 0.0)
// evenly divisible
else
// not evenly divisible
Es posible que desee comparar el resultado de fmod con un valor pequeño en lugar de 0.0 según sus necesidades.
- Escanéalos como dobles y llámalos x1 y x2
- Encuentra lo que x1 / x2 está usando división y llámalo x3
- Encuentre x1 - (x2 * x3) y vea si ese número es lo suficientemente cercano a cero - si es así, entonces x1 es divisible por x2 - (obviamente teniendo en cuenta la posibilidad de valores negativos aquí)
lol - línea 3 fija :)
El concepto de "número par" solo se define para enteros. No se puede aplicar a los dobles; no tiene sentido matemático De Wikipedia :
Un número par es un número entero que es "divisible uniformemente" por 2, es decir, divisible por 2 sin el resto.
Le sugiero que convierta sus dobles a ints, aplicando el método que decida (redondeo, truncamiento) y luego utilice el módulo como sugiera.
La familia de funciones fmod () da resultados terribles. Supongamos que desea determinar si 42 es divisible por 0,4. Es, 105 veces. Sin embargo, fmod hace la división y obtiene un resultado como 104.99999 que luego se redondea a 104, lo que da como resultado un resto de 0.399999 que da un resultado falso negativo. remainderl (), sin embargo, parece funcionar. Incluso el 0,4 en sí está representado de manera inexacta en punto flotante.
Para las personas que no asimilan el concepto de "divisible uniformemente", no tiene nada que ver con que el resultado sea un número par, probablemente tenga su etimología al revés. Los números pares son aquellos números que son divisibles por 2. Y el concepto de divisibilidad es totalmente válido para los no enteros. Divisible uniformemente significa que el resultado de la división es un número entero independientemente de si el dividendo o el divisor son. Una aplicación de ejemplo es si tiene un torno de metal con un tornillo de avance de 3 mm y está cortando un perno de 0,4 mm. 14 hilos a 3 mm alineados con 105 hilos a 0,4 mm. El cálculo de divisibilidad se utiliza para indicar dónde se sincronizan nuevamente las distintas partes móviles del torno para que pueda volver a acoplar para la siguiente pasada de corte. Otro ejemplo son las medidas imperiales que se han convertido a métricas. 50.8mm (2 ") es divisible por 25.4mm (1"). Incluso sin conversiones métricas, las dimensiones a menudo no son enteros, pero la divisibilidad es a menudo un problema: 0.5 "es divisible por 0.1", 0.125 "y 0.250". La conversión de un número de punto flotante (como 0.375 ") a una representación fraccionaria (3/8") es una aplicación más de la divisibilidad a números no enteros.
Los dos cálculos alternativos en esta función de muestra dan los mismos resultados para cientos de pares de números diferentes. Sin embargo, reemplazar remainderl () con fmodl () o roundl () con floorl () da muchos resultados no válidos. Originalmente utilicé un fuzz de 0.001. El error de cálculo real parece ser generalmente de orden 1E-15, por lo que se puede usar un fuzz más pequeño. Sin embargo, comparar el resultado con 0.0 dará resultados negativos falsos. Es posible que desee expresar su fuzz en términos de su denominador en caso de que esté trabajando con números muy pequeños. divisible (42, 0.4) y divisible (41,0.4) debe dar los mismos resultados que divisible (0.000000042, 0.0000000004) y divisible (0.000000041, 0.0000000004). ¿IE son 42nm y 41nm divisibles por 0.4nm? Con la versión de la función dada aquí, lo hacen. Con un fuzz fijo, no necesariamente. Sin embargo, divisible (42, 0.0000000004) todavía da un falso negativo (el error es 1.53003e-15, que es más grande que el fuzz de 4E-19), por lo que la comparación de números que difieren en 9 órdenes de magnitud no es confiable. El punto flotante IEEE tiene sus limitaciones. Note que usé cálculos dobles largos para minimizar los errores de cálculo y representación. Esta función no fue probada con números negativos.
int divisible(long double a, long double b)
{
int result;
#if 1
if(fabsl(((roundl(a/b)*b)- a)) <= (1E-9*b) ) {
result=TRUE;
} else {
result=FALSE;
}
#else
if( fabsl(remainderl(a,b)) <= (1E-9*b ) ){
result=TRUE;
} else {
result=FALSE;
}
#endif
// printf("divisible(%Lg, %Lg): %Lg, %Lg,%d/n", a, b, roundl(a/b), fabsl(((roundl(a/b)*b)-a)), result);
return(result);
}
No estoy seguro de lo que está tratando de hacer, pero he usado fmod () de math.h en el código de síntesis de audio, donde necesitaba que mis parámetros fueran flotantes o dobles y necesitaba un módulo.
Si quieres ser absolutamente preciso, podrías usar matemática de punto fijo. Es decir, haga todo con ints, pero los ints que son (en su caso) una potencia de 10 del valor que realmente representan.
Digamos que el usuario ingresa 123.45 y 6789.1. Primero, debes asegurarte de tener la misma cantidad de lugares decimales, así que agrega ceros al final con los que tienen menos lugares decimales. Eso nos da 123.45 y 6789.10 (ahora ambos con 2 decimales). Ahora simplemente elimine el punto decimal, para obtener 12345 y 678910. Si uno se divide en el otro de manera uniforme, esa es su respuesta.
Esto funciona porque la eliminación del punto decimal multiplica ambos por la misma constante (100 en el ejemplo anterior). (x * 100) / (y * 100) == x / y
Algunas cosas a tener en cuenta: si lee la parte entera y la parte fraccionaria como ints, tenga cuidado de no perder ceros en la parte fraccionaria. (Por ejemplo: ¡0.1 y 0.0001 no son el mismo número!) Además, si hay suficientes decimales, puede desbordar. Probablemente quieras al menos usar un largo.
También podrías hacer el cálculo con dobles, pero será menos preciso. Para hacerlo de esa manera, haga la división y luego compare la diferencia entre el resultado y el resultado redondeado. Si dentro de alguna pequeña tolerancia, entonces se divide uniformemente.
El encabezado estándar math.h
define las siguientes funciones:
-
double fmod(double x, double y);
-
float fmodf(float x, float y);
-
long double fmodl(long double x, long double y);
Estas funciones devuelven el resultado del resto de x
dividido por y
. El resultado tiene el mismo signo que el de x
. Puedes usar r = fmod(x, y);
para los números double
x
e y
, y compruebe si r == 0
. Si no desea probar la divisibilidad exacta pero agregar cierta tolerancia, puede verificar si r
está "lo suficientemente cerca" a 0 o y
(gracias caf).
fmodf()
y fmodl()
son nuevos en C99.
Edición : C99 también define un remainder(double x, double y)
separado remainder(double x, double y)
, que devuelve el resto de x/y
. De http://docs.sun.com/source/806-3568/ncg_lib.html :
El
remainder(x,y)
es la operación especificada en la norma IEEE 754-1985. La diferencia entre elremainder(x,y)
yfmod(x,y)
es que el signo del resultado devuelto por elremainder(x,y)
podría no coincidir con el signo defmod(x,y)
y
, mientras quefmod(x,y)
Siempre devuelve un resultado cuyo signo concuerda conx
. Ambas funciones devuelven resultados exactos y no generan excepciones inexactas.
...
Cuando
y
≠ 0, el restor = x REM y
se define independientemente del modo de redondeo mediante la relación matemáticar = x - ny
, donden
es el entero más cercano al valor exacto dex/y
; cuando| n - x/y | = 1/2
| n - x/y | = 1/2
| n - x/y | = 1/2
, entoncesn
es par. Así, el resto es siempre exacto. Sir = 0
, su signo será el dex
. Esta definición es aplicable para todas las implementaciones.
(Tanto fmod()
como el remainder()
deberían funcionar para usted).