2016-05-06 46 views
5

Próbuję wydrukować liczbę zmiennoprzecinkową w zespole x86_64, ale po prostu wypisuje wartość jako zero.Sposób drukowania pojedynczej precyzji zmiennoprzecinkowej za pomocą printf

Jest już kilka pytań na ten temat. Jedna okazała się być rozwiązana, zapewniając, że you set the number of vector registers you're using in %al. Inny pokazał, że you need to have a stack alignment of 16 bytes. Ale robię obie te rzeczy i nadal nie otrzymuję poprawnego wyniku.

To jest mój program:

# 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 

Odpowiedz

5

printf(3)'s %f format specifier wants a double. Nie ma sposobu, aby uzyskać printf, aby zaakceptować float, tylko double lub long double.

domyślne promocje argumentów c za określić, że zwraca się do zmiennej liczbie argumentów funkcji, takich jak foo(char *fmt, ...) promujące float do double i wykonywać zwykłe promocjach całkowitą od wąskich typów całkowitych do int dla spływu argumenty, które pasują do ... część prototypu. (To samo dotyczy wszystkich argumentów dla funkcji wywołujących bez prototypu.) N1570 6.5.2.2 Function calls, subsections 6 and 7.

Zatem C nie zapewnia drogę dla rozmówcy zdać float do printf, tak, że nie ma dla niego konwersji i %f oznacza double. (%lf zwykle działa także dla double, zakładając, że implementacja ignoruje go dla konwersji niecałkowitych/wchar_t). Zauważ, że scanf jest inny, ponieważ te promocje nie mają wpływu na float * i double *.


W tym przypadku obciążenie z CVTSS2SD .num, %xmm0.

Jeśli spojrzeć na compiler output zobaczysz gcc zrobić wszystko, co zrobił, a pxor -ZERO rejestru najpierw przełamać fałszywy zależność od starej wartości %xmm0. (słaby projekt cvtss2sd pozostawia niezmienione 64-bitowe górne punkty docelowe.) gcc brzydzi się ostrożnością i wstawia instrukcje zerowania xor, aby w wielu przypadkach złamać fałszywe zależności.


Pewnie coraz 0 ponieważ górne bity xmm0 stało się zero. Tak więc wzór bitowy dla 123.4f w najniższych 32 bitach mantysy, gdy printf wygląda na niskie 64 bity xmm0 jako double (IEEE binary64 na x86).

Jeśli spróbujesz odpowiednika z float, (np. Na http://www.h-schmidt.net/FloatConverter/IEEE754.html), ustawiając niektóre bity w niskiej połowie, otrzymasz bardzo małą liczbę denormalną. Jeśli użyłeś %g (notacja naukowa) lub %a (hex), pojawią się niezerowe bity. (O ile w MXCSR nie włączono trybu Denormals Are Zero).

+0

To niesamowite, że pomimo pisania C++ od lat i używania printf wiele, wiele razy ... Nigdy o tym nie wiedziałem. – cgmb

+1

@ cgmb: tak, to nie oczywiste. Zapominam, ale może nie być nawet możliwe przekazanie 'float' do funkcji var-args w C. Reguły promocji określają konwersję na' double'. Spodziewam się, że gdyby było to możliwe, przynajmniej GNU C miałby specyfikator formatu. (Jak '% hf' lub coś podobnego).Dla mnie naprawdę ograniczałem się do tego ograniczenia, patrząc na asm, ponieważ w przeciwnym razie kompilator zawsze dokonuje konwersji dla ciebie. (Również, od faktycznie chcąc wydrukować 'float' z formatowaniem w stylu"% a "w formacie szesnastkowym) –

Powiązane problemy