2010-03-22 10 views
12

Voglio verificare se un valore in virgola mobile è "quasi" un multiplo di 32. Ad es. 64.1 è "quasi" divisibile per 32, e così è 63.9."Quasi divisibile"

In questo momento mi sto facendo questo:

#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 
} 

un modo migliore per fare questo?

risposta

3

beh, si potrebbe ritagliare il secondo file fmodf semplicemente sottraendo 32 un'altra volta per ottenere il mod dal basso.

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

+1 Questa è una soluzione molto ragionevole se non si dispone della funzione 'remainder' disponibile. –

+2

o: else if (32.0f - offset quinmars

2

In un'implementazione conforme allo standard C, si potrebbe utilizzare la funzione remainder anziché fmod:

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

Se si è su una piattaforma non conforme (MSVC++, per esempio), allora remainder non è disponibile, purtroppo. Penso che la risposta di fastmultiplication sia abbastanza ragionevole in quel caso.

+0

Cool ma, non ho http://www.digitalmars.com/rtl/fltpnt.html#remainder su MSVC++. . ? – bobobobo

+1

'remainder' è nello standard C per 10 anni e nello standard IEEE-754 per 25 anni. MSVC non lo fornisce davvero? È possibile rimuovere il tag "C" dalla domanda, poiché MSVC++ non è un compilatore C =) –

+0

@Stephen 'fmodf' è l'equivalente del promemoria in MSC++ (http://msdn.microsoft.com/en-us/ library/20dckbeh (VS.71) .aspx) –

0

Per quello che raccolgo, vuoi rilevare se un numero è quasi divisibile per altri, giusto?

farei qualcosa di simile:

#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

Il tuo codice tratta * NEARLY \ _DIVISIBLE * come percentuale, mentre è un valore assoluto per l'OP. Perché ti sei moltiplicato per uno negativo invece di usare '-remainder'? –

+0

oops ... risolto! Grazie! –

0

Penso che sia giusto:

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

Nulla in questa domanda è Java; il codice nella domanda è C. –

+0

Mi sento depresso, ero sicuro che fosse java: O –

2

si parla che si deve testare quasi divisibilità con . La seguente teoria dovrebbe valere per i test quasi divisibilità contro potenze di due:

#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); 
} 

Quello che stiamo facendo è costringere il galleggiante, e galleggiante + SOGLIA a lungo.

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

Ora testiamo se (lungo) f è divisibile con 32. Basta controllare i cinque bit più bassi, se sono tutti a zero, il numero è divisibile per 32. Questo porta ad una serie di falsi positivi : 64.2 a 64.8, quando convertiti in long, sono anche 64 e passerebbero il primo test. Quindi, controlliamo se la differenza tra la loro forma troncata ef è minore o uguale a THRESHOLD.

Anche questo ha un problema: f - (float) l2 < = THRESHOLD è valido per 64 e 64.1, ma non per 63.9. Quindi, aggiungiamo un'eccezione per i numeri inferiori a 64 (che, quando incrementati di THRESHOLD e successivamente costretti a long - annotare che il test in discussione deve essere inclusivo con il primo test - è divisibile per 32), specificando che i 5 bit inferiori non sono zero. Ciò vale per 63 (1000000 - 1 == 1).

Una combinazione di questi tre test indica se il numero è divisibile per 32 o meno. Spero che questo sia chiaro, per favore perdona il mio strano inglese.

Ho appena testato l'estensibilità ad altre potenze di tre - il seguente programma visualizza numeri compresi tra 383,5 e 388,4 che sono divisibili per 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); 
} 

sembra funzionare bene finora!

0

Perché non dovresti semplicemente dividere per 32, quindi arrotondare e prendere la differenza tra il numero arrotondato e il risultato effettivo?

Qualcosa di simile (perdonate il codice non testato/pseudo, il tempo di ricerca):

#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 
} 

Se ancora voleva fare controlli dall'alto e dal basso, è possibile rimuovere gli addominali, e controllare per vedere se la differenza è> 0 o < 0.

0

Questo è senza uing il fmodf due volte.

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; 
} 
Problemi correlati