2016-07-08 48 views
5

ho cercato di stampare utilizzando galleggiante %d (so che questo non dovrebbe essere fatto. Ma ogni volta re-run eseguibile dà un valore diverso)
La mia domanda è: Perché il valore stampato cambia ogni volta? My System: Ubuntu 14.04 (64 bit) Compiler: 4.8.4 Ecco il codice:C virgola mobile cambia il valore% d ogni volta eseguita

#include<stdio.h> 

int main(){ 
    float b = 12.3456; 

    printf("%d\n",b); 

} 

Esempio di output:

[email protected]:~/C-fi$ ./test 
-1629995944 
[email protected]:~/C-fi$ ./test 
1147348376 
[email protected]:~/C-fi$ ./test 
-1746005432 
[email protected]:~/C-fi$ ./test 
510102216 
[email protected]:~/C-fi$ 
+8

Un meraviglioso esempio di * comportamento non definito * - Cos'altro hai bisogno di sapere? – tofro

+0

Voglio sapere perché è successo? – 4bh1

+0

Perché non è definito? – tofro

risposta

7

Probabilmente valore in virgola mobile viene fatto passare attraverso il registro FPU, ma printf() prova a leggere un intero da qualche altra parte, dove si aspetta di vedere un numero intero (stack o altro registro). E il contenuto di quel posto non è specificato. Rigorosamente dicendo (come ha scritto @dbush) il tuo codice causa un comportamento indefinito.

+0

Questo non corrisponderebbe a nessun ABI Lo so, ma alcuni ABI passano valori in virgola mobile da stampare con% f e valori interi da stampare con '% d' in entrambi i registri, il che potrebbe spiegare ciò che vediamo in base al codice precedente (e ASLR). –

+1

ad es. per x64, https://msdn.microsoft.com/en-us/library/ms235286.aspx dice "Gli argomenti vengono passati nei registri RCX, RDX, R8 e R9. Se gli argomenti sono float/double, vengono passati in XMM0L, XMM1L, XMM2L e XMM3L. " Quindi printf si aspetta un float in XMM0L ma quel registro non è mai stato impostato dal tuo codice. – moonshadow

+1

@PascalCuoq L'ABI di SysV per amd64 è come questo. – fuz

8

Utilizzare l'identificatore di formato errato per printf è un classico esempio di undefined behavior. Ciò significa che il comportamento è imprevedibile e non può dipendere da essere coerente da una corsa all'altra. Potrebbe bloccarsi, potrebbe stampare valori casuali, o potrebbe sembrare funzionare.

In realtà ciò che sta accadendo è un dettaglio di implementazione del compilatore e dell'architettura in questione. Ma a meno che tu non stia effettivamente scrivendo un compilatore, non serve molto a scavare in esso.

Se hai familiarità con l'assemblatore, puoi guardare il codice compilato per vedere quali istruzioni ha generato. Tieni presente, tuttavia, che diversi compilatori (anche lo stesso compilatore con diverse impostazioni di ottimizzazione) genereranno molto probabilmente assemblaggi diversi.

1

Come indicato nei commenti, questo suona come comportamento non definito, che spiega (beh, una sorta di) cosa sta succedendo.

Nel caso in cui si desidera leggere la rappresentazione interna di un galleggiante, o se si vuole fare un po 'hack di basso livello sul numero, c'è semplici modi per farlo, ad esempio:

union { 
    float f; 
    int i; 
} n; 

n.f = 12.3456; 
printf("%d\n", n.i); // should be the same number each time 

Si noti che questo funziona solo se float e int hanno la stessa dimensione sul tuo sistema, ma che dovrebbe corrispondere a per la maggior parte delle persone. È possibile aggiungere un assert(sizeof(float) == sizeof(int)) se si vuole essere sicuro.

+0

Rigorosamente dicendo che questo 'union' hack non è portabile almeno perché i campi non possono sovrapporsi a causa delle diverse regole di allineamento. Sarebbe meglio farlo con 'memcpy()'. – Sergio

+1

@Serhio All'inizio di un 'unione' non ci sono mai padding, quindi perché i membri non si sovrappongono? ISO/IEC 9899: 2011 § 6.7.2.1 (15) '... Potrebbe esserci un padding senza nome all'interno di un oggetto di struttura, ma non all'inizio. In effetti, questo non tiene conto degli oggetti union. –

+0

Oltre al mio commento precedente, §6.5.3.4 (4) su 'sizeof':' ... Quando viene applicato a un operando con struttura o tipo di unione, il risultato è il numero totale di byte in tale oggetto, inclusi interni e trailing padding. Non menziona il padding principale, il che suggerisce che non esiste. –

Problemi correlati