2011-11-09 19 views
7

Mi sono imbattuto nel seguente esempio su wikipedia (http://en.wikipedia.org/wiki/Type_conversion#Implicit_type_conversion).Conversione implicita del tipo in C

#include <stdio.h> 

int main() 
{ 
    int i_value = 16777217; 
    float f_value = 16777217.0; 
    printf("The integer is: %i\n", i_value); // 16777217 
    printf("The float is: %f\n", f_value); // 16777216.000000 
    printf("Their equality: %i\n", i_value == f_value); // result is 0 
} 

loro spiegazione: "Questo comportamento dispari è causato da un cast implicito di i_value galleggiare quando viene confrontato con f_value; un cast che perde precisione, rendendo i valori a confronto diverso"

Non è sbagliato? Se i_value venisse eseguito il cast, allora entrambi avrebbero la stessa perdita di precisione e sarebbero uguali. Quindi i_value deve essere castato per raddoppiare.

+0

Con g ++ (GCC 4.6.2) ottengo '1' per l'uguaglianza. –

+0

@Kerrek: E io. In VS, ottengo 0. –

+0

@OliCharlesworth: Sono curioso di cambiare il letterale in 'f' o il tipo in' double' - Ottengo '1' in tutti i casi ... –

risposta

7

n, nel caso dell'operatore di uguaglianza, i "conversioni aritmetiche abituali" verificano, che partono:

  • primo luogo, se il corrispondente tipo reale di uno degli operandi è long double, la l'altro operando viene convertito, senza modificare il dominio di tipo , in un tipo il cui tipo reale corrispondente è long double.
  • In caso contrario, se il corrispondente tipo reale di entrambi gli operandi è double, l'altro operando viene convertito, senza modificare il tipo di dominio , in un tipo il cui tipo reale corrispondente è double.
  • Altrimenti, se il corrispondente tipo reale di entrambi gli operandi è float, l'altro operando viene convertito, senza modificare il tipo di dominio , in un tipo il cui tipo reale corrispondente è float.

Quest'ultimo caso si applica qui: i_value viene convertito in float.

La ragione per cui si può vedere un risultato dispari dal confronto, nonostante questo, è causa di questo avvertimento alle usuali conversioni aritmetiche:

I valori di operandi galleggianti e dei risultati di galleggiante Le espressioni possono essere rappresentate con maggiore precisione e gamma rispetto a richieste dal tipo; i tipi non sono modificati in tal modo.

Questo è ciò che sta accadendo: il tipo di conversione i_value è ancora float, ma in questa espressione il compilatore sta approfittando di questa latitudine e lo rappresenta in maggior precisione rispetto float. Questo è tipico comportamento del compilatore durante la compilazione per il floating point 387 compatibile, poiché il compilatore lascia i valori temporanei sullo stack in virgola mobile, che memorizza i numeri in virgola mobile in un formato di precisione estesa a 80 bit.

Se il compilatore è gcc, è possibile disattivare questa precisione aggiuntiva assegnando l'opzione da riga di comando -ffloat-store.

+0

In x64 gcc utilizza l'istruzione cvtsi2ssl esplicita che converte l'intero in virgola mobile. Su x86, tuttavia, questo è esattamente ciò che accade e che una precisione maggiore è addirittura maggiore del doppio. –

+0

@ konrad.kruczynski: Sì, e puoi ottenere lo stesso risultato su x86 fornendo l'opzione '-mfpmath = sse' (che richiede anche' -msse' o un'opzione che lo sottintende). – caf

-1

Credo che il valore intero più grande che può contenere un punto mobile IEEE a 32 bit sia 1048576, che è inferiore al numero precedente. Quindi, è sicuramente vero che il valore in virgola mobile non verrà mantenuto esattamente 16777217.

La parte di cui non sono sicuro è come il compilatore effettua il confronto tra due diversi tipi di numeri (ad esempio un float e un int) . Mi viene in mente tre modi diversi questo potrebbe essere fatto:

1) convertire entrambi i valori a "galleggiare" (questo dovrebbe rendere i valori lo stesso, quindi questo non è quello che fa il compilatore)

2) è probabilmente Converti entrambi i valori in "int" (questo può o non può mostrare loro lo stesso ... la conversione in un int spesso tronca, quindi se il valore in virgola mobile è 16777216.99999, la conversione in "int" troncerebbe)

3) Converti entrambi i valori in "doppio". La mia ipotesi sarebbe che questo è ciò che farebbe il compilatore. Se questo è ciò che fa il compilatore, i due valori sarebbero sicuramente diversi. Un doppio può contenere esattamente 16777217 e potrebbe anche rappresentare esattamente il valore in virgola mobile a cui converte 16777217.0 (che non è esattamente 16777217.0).

+3

1048576 è 2^20, quindi non è corretto. –

0

Ci sono alcune buone risposte qui. Devi fare molta attenzione a convertire tra vari numeri interi e varie rappresentazioni in virgola mobile.

Generalmente non eseguo il test dei numeri in virgola mobile per l'uguaglianza, specialmente se uno di questi proviene da un cast implicito o esplicito di un tipo intero. Lavoro su un'applicazione che è piena di calcoli geometrici. Per quanto possibile, lavoriamo con interi normalizzati (forzando una precisione massima che accetteremo nei dati di input). Per i casi in cui è necessario utilizzare il punto mobile, applicheremo un valore assoluto alla differenza se è necessario un confronto.

Problemi correlati