2016-05-06 28 views
5

Ich versuche, eine Gleitkommazahl in x86_64 Assembly zu drucken, aber es druckt nur den Wert als Null.So drucken Sie ein Float mit einfacher Genauigkeit mit printf

Es gibt ein paar Fragen dazu bereits. Einer schien gelöst zu sein, indem sichergestellt wurde, dass you set the number of vector registers you're using in %al. Ein anderer zeigte, dass you need to have a stack alignment of 16 bytes. Aber ich mache beides und bekomme immer noch keine korrekte Ausgabe.

Das ist mein Programm:

# 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 

Antwort

5

printf(3)'s %f format specifier wants a double. Es gibt keine Möglichkeit, printf dazu zu bringen, eine float, nur double oder long double zu akzeptieren.

der C Standardargument aktionen angeben, die Funktionen wie foo(char *fmt, ...) fördern float zu double, und führen Sie die üblichen ganzzahligen aktionen von schmalen Integer-Typen zu int, für Hinter args variadische aufruft, die den ... Teil des Prototyps entsprechen. (Gleiches gilt für alle Argumente zum Aufruf von Funktionen ohne Prototyp.) N1570 6.5.2.2 Function calls, subsections 6 and 7.

Somit bietet C keine Möglichkeit für einen Anrufer eine passieren floatprintf zu, so hat es keine Umwandlung für sie, und %fdouble bedeutet. (%lf funktioniert normalerweise auch für double, vorausgesetzt, die Implementierung ignoriert es für nicht ganzzahlige/wchar_t Konvertierungen). Beachten Sie, dass sich scanf davon unterscheidet, da float * und double * nicht von diesen Werbeaktionen betroffen sind.


In diesem Fall Last mit CVTSS2SD .num, %xmm0.

Wenn Sie bei compiler output anschauen, werden Sie sehen gcc alles tun, du hast, und pxor -Nullsignal das Register zuerst die falsche Abhängigkeit von dem alten Wert von %xmm0 zu brechen. (Das schlechte Design von cvtss2sd lässt die oberen 64 Bits des Ziels unverändert.) gcc irrt auf der Seite der Vorsicht und fügt xor-Nullsetzungsbefehle ein, um in vielen Fällen falsche Abhängigkeiten zu brechen.


Du bist wahrscheinlich immer 0, weil die oberen Bits xmm0 Null sein passieren. Also das Bitmuster für 123.4f in den niedrigen 32 Bits der Mantisse, wenn printf die niedrigen 64 Bits von xmm0 als double betrachtet (IEEE binary64 auf x86).

Wenn Sie das Äquivalent mit einem float versuchen (z. B. unter http://www.h-schmidt.net/FloatConverter/IEEE754.html), setzen Sie einige Bits in der unteren Hälfte, und Sie erhalten eine sehr kleine Denormalzahl. Wenn Sie %g (wissenschaftliche Notation) oder %a (Hex) verwendet haben, würden die Nicht-Null-Bits angezeigt. (Es sei denn, Sie hatten Denormals Are Zero-Modus in der MXCSR aktiviert.)

+0

Es ist erstaunlich für mich, dass, obwohl C++ seit Jahren geschrieben und printf viele, viele Male verwendet ... Ich wusste das nie. – cgmb

+1

@cgmb: Ja, es ist nicht offensichtlich. Ich vergesse, aber es ist vielleicht nicht einmal möglich, ein "float" an eine var-args-Funktion in C zu übergeben. Die Promotionsregeln spezifizieren die Umwandlung in "double". Ich denke, wenn es möglich wäre, hätte zumindest GNU C einen Formatbezeichner dafür. (Wie '% hf' oder etwas).Für mich hat ich diese Einschränkung nur von asm aus betrachtet, da sonst der Compiler immer die Konvertierung für Sie übernimmt. (Auch von eigentlich aus wollen einen 'float' mit'% a' hex-style Formatierung drucken) –

Verwandte Themen