2010-03-22 10 views
12

Quiero comprobar si un valor de coma flotante es "casi" un múltiplo de 32. Por ejemplo, 64.1 es "casi" divisible por 32, y también lo es 63.9."Casi divisible"

En este momento estoy haciendo esto:

#define NEARLY_DIVISIBLE 0.1f 
float offset = fmodf(val, 32.0f) ; 
if(offset < NEARLY_DIVISIBLE) 
{ 
    // its near from above 
} 
// if it was 63.9, then the remainder would be large, so add some then and check again 
else if(fmodf(val + 2*NEARLY_DIVISIBLE, 32.0f) < NEARLY_DIVISIBLE) 
{ 
    // its near from below 
} 

Tienes una mejor manera de hacer esto?

Respuesta

3

Bueno, podría cortar el segundo fmodf restando 32 una vez más para obtener el mod de abajo.

if(offset < NEARLY_DIVISIBLE) 
    { 
     // it's near from above 
    } 
    else if(offset-32.0f>-1*NEARLY_DIVISIBLE) 
    { 
     // it's near from below 
    } 
+0

+1 Esta es una solución muy razonable si no tiene disponible la función 'remainder'. –

+2

o: else if (32.0f - offset quinmars

2

En una implementación compatible con el estándar-C, se podría utilizar la función de remainder en lugar de fmod:

#define NEARLY_DIVISIBLE 0.1f 
float offset = remainderf(val, 32.0f); 
if (fabsf(offset) < NEARLY_DIVISIBLE) { 
    // Stuff 
} 

Si uno está en una plataforma no conforme (MSVC++, por ejemplo), entonces remainder no está disponible, por desgracia. Creo que la respuesta de la rápida multiplicación es bastante razonable en ese caso.

+0

Genial, pero no tengo http://www.digitalmars.com/rtl/fltpnt.html#remainder en MSVC++. . ? – bobobobo

+1

'remainder' ha estado en el estándar C durante 10 años, y en el estándar IEEE-754 durante 25 años. ¿Realmente MSVC no lo proporciona? Puede eliminar la etiqueta 'C' de la pregunta, ya que MSVC++ no es un compilador de C =) –

+0

@Stephen 'fmodf' es el equivalente de un recordatorio en MSC++ (http://msdn.microsoft.com/en-us/ library/20dckbeh (VS.71) .aspx) –

0

Por lo que veo, ¿desea detectar si un número es casi divisible por otro, ¿verdad?

me gustaría hacer algo como esto:

#define NEARLY_DIVISIBLE 0.1f 

bool IsNearlyDivisible(float n1, float n2) 
{ 
    float remainder = (fmodf(n1, n2)/n2); 
    remainder = remainder < 0f ? -remainder : remainder; 
    remainder = remainder > 0.5f ? 1 - remainder : remainder; 
    return (remainder <= NEARLY_DIVISIBLE); 
} 
+0

Su código trata * CASI \ _DIVISIBLE * como un porcentaje, mientras que es un valor absoluto para el OP. ¿Por qué multiplicaste por negativo en lugar de usar '-remainder'? –

+0

¡Uy ... arreglado! ¡Gracias! –

0

Creo que es justo:

bool nearlyDivisible(float num,float div){ 
float f = num % div; 
if(f>div/2.0f){ 
f=f-div; 
} 
f=f>0?f:0.0f-f; 
return f<0.1f; 
} 
+0

Nada en esta pregunta es Java; el código en la pregunta es C. –

+0

Me siento deprimido, estaba seguro de que era java: O –

2

usted menciona que usted tiene que probar casi divisibilidad con . La siguiente teoría debería ser cierto para las pruebas a corto divisibilidad contra potencias de dos:

#define THRESHOLD 0.11 
int nearly_divisible(float f) { 
    // printf(" %f\n", (a - (float)((long) a))); 
    register long l1, l2; 
    l1 = (long) (f + THRESHOLD); 
    l2 = (long) f; 
    return !(l1 & 31) && (l2 & 31 ? 1 : f - (float) l2 <= THRESHOLD); 
} 

Lo que estamos haciendo es coaccionar el flotador, y el flotador + UMBRAL a tiempo.

f  (long) f (long) (f + THRESHOLD) 
63.9 63   64 
64  64   64 
64.1 64   64 

Ahora comprobamos si (largo) f es divisible con 32. Sólo tienes que comprobar los menores cinco bits, si se ajustan a cero, el número es divisible por 32. Esto conduce a una serie de falsos positivos : 64.2 a 64.8, cuando se convierte a largo, también son 64, y pasarían la primera prueba. Por lo tanto, verificamos si la diferencia entre su forma truncada yf es menor o igual que UMBRAL.

Esto, también, tiene un problema: f - (flotación) l2 < = UMBRAL sería válido para 64 y 64.1, pero no para 63.9. Por lo tanto, agregamos una excepción para números menores que 64 (que, cuando se incrementa por UMBRAL y posteriormente se coacciona para indicar por mucho tiempo que la prueba en discusión debe ser inclusiva con la primera prueba, es divisible por 32), al especificar que los 5 bits inferiores no son cero. Esto se aplicará a 63 (1000000 - 1 == 1).

Una combinación de estas tres pruebas indicaría si el número es divisible por 32 o no. Espero que esto esté claro, por favor perdona mi extraño inglés.

Acabo de probar la capacidad de extensión a otras potencias de tres - los siguientes números de programa imprime entre 383,5 y 388,4 que son divisibles por 128.

#include <stdio.h> 

#define THRESHOLD 0.11 

int main(void) { 
    int nearly_divisible(float); 
    int i; 
    float f = 383.5; 
    for (i=0; i<50; i++) { 
     printf("%6.1f %s\n", f, (nearly_divisible(f) ? "true" : "false")); 
     f += 0.1; 
    } 
    return 0; 
} 

int nearly_divisible(float f) { 
    // printf(" %f\n", (a - (float)((long) a))); 
    register long l1, l2; 
    l1 = (long) (f + THRESHOLD); 
    l2 = (long) f; 
    return !(l1 & 127) && (l2 & 127 ? 1 : f - (float) l2 <= THRESHOLD); 
} 

parece funcionar bien hasta ahora!

0

¿Por qué no solo dividir por 32, luego redondear y tomar la diferencia entre el número redondeado y el resultado real?

Algo así como (valga el código no probado/seudo, no hay tiempo para las operaciones de búsqueda):

#define NEARLY_DIVISIBLE 0.1f 
float result = val/32.0f; 
float nearest_int = nearbyintf(result); 
float difference = abs(result - nearest_int); 
if(difference < NEARLY_DIVISIBLE) 
{ 
    // It's nearly divisible 
} 

Si todavía quería hacer comprobaciones de arriba y de abajo, usted podría quitar el ABS, y comprobar para ver si la diferencia es> 0 o < 0.

0

Esto es sin usar el fmodf dos veces.

int main(void) 
{ 
    #define NEARLY_DIVISIBLE 0.1f 
    #define DIVISOR 32.0f 
    #define ARRAY_SIZE 4 
    double test_var1[ARRAY_SIZE] = {63.9,64.1,65,63.8}; 
    int i = 54; 
    double rest; 
    for(i=0;i<ARRAY_SIZE;i++) 
    { 
     rest = fmod(test_var1[i] ,DIVISOR); 
     if(rest < NEARLY_DIVISIBLE) 
     { 
      printf("Number %f max %f larger than a factor of the divisor:%f\n",test_var1[i],NEARLY_DIVISIBLE,DIVISOR); 
     } 
     else if(-(rest-DIVISOR) < NEARLY_DIVISIBLE) 
     { 
      printf("Number %f max %f less than a factor of the divisor:%f\n",test_var1[i],NEARLY_DIVISIBLE,DIVISOR); 
     } 
    } 
    return 0; 
} 
Cuestiones relacionadas