2016-05-06 16 views
5

Sto provando a stampare un numero in virgola mobile nell'assemblaggio x86_64 ma si stampa semplicemente il valore come zero.Come stampare un float a precisione singola con printf

Ci sono già alcune domande su questo. Uno sembrava essere risolto garantendo che you set the number of vector registers you're using in %al. Un altro ha mostrato che you need to have a stack alignment of 16 bytes. Ma, sto facendo entrambe queste cose e ancora non ottengo risultati corretti.

Questo è il mio programma:

# prints a floating point value 
.section .rodata 
.fmt: .string "num: %f\n" 
.num: .float 123.4 

.section .text 
.global main 
.type main, @function 
main: 
    subq $8, %rsp  # 16-byte alignment 

    # print my number 
    movss .num, %xmm0 # load float value 
    movq $.fmt, %rdi # load format string 
    movb $1, %al  # use 1 vector register 
    call printf 

    # exit 
    addq $8, %rsp  # undo alignment 
    movq $0, %rax  # return 0 
    ret 

risposta

5

printf(3)'s %f format specifier wants a double. Non è possibile ottenere che printf accetti uno float, solo double o long double.

promozioni argomento default di C specificano che le chiamate a funzioni come variadic foo(char *fmt, ...) promuovono float a double, ed eseguono le solite promozioni interi di tipi interi strette per int, per trailing args che corrispondono alla parte ... del prototipo. (Lo stesso vale per tutti gli argomenti per la chiamata di funzioni senza alcun prototipo.) N1570 6.5.2.2 Function calls, subsections 6 and 7.

Così C fornisce alcun modo per un chiamante passare un float a printf, quindi ha alcuna conversione per esso, e %f significa double. (%lf di solito funziona anche per double, supponendo che l'implementazione lo ignori per le conversioni non interi/wchar_t). Si noti che scanf è diverso, perché float * e double * non sono interessati da tali promozioni.


In questo caso, carico con CVTSS2SD .num, %xmm0.

Se si guarda alla compiler output, vedrete gcc fare quello che hai fatto, e pxor -Zero registro primo a rompere la falsa dipendenza il vecchio valore di %xmm0. (Il design scarso di cvtss2sd lascia invariati i 64 bit superiori della destinazione.) gcc eroga sul lato della cautela e inserisce istruzioni xor-zero per rompere le false dipendenze in molti casi.


Probabilmente stai ricevendo 0 perché i bit superiori di xmm0 capita di essere pari a zero. Quindi il pattern di bit per 123.4f nei 32 bit bassi della mantissa, quando printf esamina i 64 bit bassi di xmm0 come double (IEEE binary64 su x86).

Se si prova l'equivalente con un float, (ad esempio su http://www.h-schmidt.net/FloatConverter/IEEE754.html), l'impostazione di alcuni bit nella metà bassa, si ottiene un numero denormal molto piccolo. Se hai usato %g (notazione scientifica) o %a (hex), i bit diversi da zero verrebbero visualizzati. (A meno che tu non abbia abilitato la modalità Denormals Zero in MXCSR.)

+0

È incredibile per me che, nonostante abbia scritto C++ per anni e usando la stampa molte, molte volte ... non lo sapevo mai. – cgmb

+1

@cgmb: sì, non è ovvio. Dimentico, ma potrebbe anche non essere possibile passare un 'float' a una funzione var-args in C. Le regole di promozione specificano la conversione in' double'. Mi aspetto che se fosse possibile, almeno GNU C avrebbe un identificatore di formato per questo. (Come '% hf' o qualcosa del genere).Per quanto mi riguarda, mi sono davvero limitato a questa limitazione osservando asm, poiché altrimenti il ​​compilatore fa sempre la conversione per te. (Inoltre, in realtà dal voler stampare un 'float' con'% a' formattazione in stile hex) –

Problemi correlati