2013-07-30 8 views
5

Ho il seguente codice per quartili trovare:I multipli di 0,25 sono esattamente rappresentabili come doppi?

#include <stdio.h> 
#include <stdlib.h> 

typedef struct { 
    double qrt[3]; 
    double *value; 
    int count; 
} t_data; 

static void set_qrt(t_data *data, int qrt) 
{ 
    int n, e; 
    double d; 

    d = qrt * 0.25 * data->count + 0.5; 
    n = (int)d; 
    e = n != d; 
    data->qrt[qrt - 1] = data->value[n - 1]; 
    if (e) { 
     data->qrt[qrt - 1] += data->value[n]; 
     data->qrt[qrt - 1] *= 0.5; 
    } 
} 

static void set_qrts(t_data *data) 
{ 
    set_qrt(data, 2); 
    if (data->count > 1) { 
     set_qrt(data, 1); 
     set_qrt(data, 3); 
    } else { 
     data->qrt[0] = 0.0; 
     data->qrt[2] = 0.0; 
    } 
} 

static int comp(const void *pa, const void *pb) 
{ 
    const double a = *(const double *)pa; 
    const double b = *(const double *)pb; 

    return (a > b) ? 1 : (a < b) ? -1 : 0; 
} 

int main(void) 
{ 
    double values[] = {3.7, 8.9, 7.1, 5.4, 1.2, 6.8, 4.3, 2.7}; 
    t_data data; 

    data.value = values; 
    data.count = (int)(sizeof(values)/sizeof(double)); 
    qsort(data.value, data.count, sizeof(double), comp); 
    set_qrts(&data); 
    printf("Q1 = %.1f\nQ2 = %.1f\nQ3 = %.1f\n", data.qrt[0], data.qrt[1], data.qrt[2]); 
} 

È

d = qrt * 0.25 * data->count + 0.5; 
n = (int)d; 
e = n != d; 

garantiti per funzionare come previsto? (E == TestIntero (d))

+2

'0.25' è un doppio e' 0.25f' è un float, risultato di 'qrt * 0.25 * data-> count + 0.5;' è un doppio. –

+0

Grazie Grijesh, conosco i letterali, la domanda riguardava la rappresentazione fluttuante con multipli di 0.25 –

+0

provare: 'float qrt = .3f, count = .4f; printf ("% lu", sizeof (qrt * 0.25 * count + 0.5)); l'output dovrebbe essere == 'sizeof (double)' –

risposta

7

Numeri 0.5, 0.25, 0.125 ecc rappresentare potenze negative di due, e quindi sono rappresentabili esattamente IEEE 754 types. L'utilizzo di questi numeri non comporta errori di rappresentazione.

+0

Solo perché questi numeri sono esatti, ciò non significa che tutti i calcoli che li riguardano essere. Hai ancora bisogno di prestare attenzione alla gamma dei tuoi valori. Non ci sono scorciatoie per l'analisi numerica - se vuoi usare i float, devi stare attento. –

+2

@LeeDanielCrocker La mia risposta riguarda i poteri negativi di 2 nel contesto del problema specifico dell'OP. Non ero in grado di coprire l'analisi numerica in generale, in quanto non è né fattibile nel formato Q & A, né è appropriato in risposta a una domanda piuttosto specifica. – dasblinkenlight

+0

Ho interpretato la domanda dell'OP come "questi calcoli saranno esatti"? E la risposta giusta a questo, come con tutto l'uso del floating point, è "Beh, dipende ..." –

1

dasblinkenlight è assolutamente corretto. I tipi double/float e interi sono memorizzati in modo diverso secondo IEEE754. Guarda this per un facile tutorial se sei curioso.

+0

Buon video !! grazie –

+0

Questa non dovrebbe essere una risposta, dovrebbe essere un commento. –

3

I valori 0,5 e 0,25 saranno esatti. I valori intermedi del tuo calcolo possono essere o meno, a seconda del loro intervallo. I doppi IEEE hanno una mantissa a 52 bit, quindi rappresentano esattamente i numeri 0,25 che hanno bisogno di 50 bit o meno nella mantissa, che è di circa 15 cifre decimali.

Quindi se si aggiunge da 0,25 a 100000000000000 (10^14), si otterrà 100000000000000.25. Ma se aggiungi 0,25 a 10000000000000000 (10^16), perderai la frazione.

+0

Grazie Lee, buona cattura, mi fa pensare che il codice che uso sia fragile, puoi suggerire un altro modo per sapere se il risultato di (int * int * 0.25 + 0.5) è un intero? –

+2

(d * d * 0.25 + 0.5) sarà un numero intero solo quando d è pari. Quando d è dispari, sarà qualcosa .25 o .75.Se si desidera che il risultato sia esattamente rappresentabile, è necessario verificare che d sia inferiore a 33 milioni circa. Sono sicuro che l'hai già sentito prima, ma lo ripeto: se hai davvero bisogno che le cose siano esatte, perché usi i galleggianti? Solo perché i tuoi input e output da e verso gli esseri umani potrebbero essere in virgola mobile, non c'è motivo per cui tu rimanga con loro internamente. Utilizzare una rappresentazione che abbia senso per l'applicazione. –

+1

@DavidRF: Se vuoi verificare se 'a * b * 0.25 + 0.5' è un intero, dove' a' e 'b' sono interi, allora quello che stai testando è' (a * b) mod 4' è uguale a 2. In C che è '(a * b)% 4 == 2 || (a * b)% 4 == -2', purché 'a * b' non trabocchi. – caf

1

Il formato a virgola mobile a precisione doppia ha 53 bit nella sua manitissa di cui uno è implicito. Ciò significa che può rappresentare tutti gli interi positivi e negativi nell'intervallo da 2^0 a 2^53-1.

0 (zero) è un caso speciale che ha il proprio formato.

Quando si tratta di una spaziatura di 0,25, l'intervallo viene calcolato in modo rettilineo da 2^-2 a 2^51-0,25. Ciò significa che parecchi, ma in nessun modo tutti i multipli di 0,25 sono esattamente rappresentabili nel formato a doppia precisione, proprio come un numero piuttosto elevato ma non tutti gli interi sono esattamente rappresentabili.

Quindi se si dispone di una spaziatura rappresentabile di 2^x l'intervallo rappresentabile è 2^x a 2^(53 + x) -2^x.

Problemi correlati