Fließkommazahlen mit SSE
Ganzzahl-Register (rax ...) können keine Kommazahlen rechnen. Dafür gibt es 16 XMM-Register (xmm0 bis xmm15, je 128 Bit) und die SSE2-Befehle. Für double (64 Bit) enden sie auf sd (scalar double):
| Befehl | Bedeutung |
|---|---|
movsd xmm0, [x] | double laden/speichern |
addsd, subsd, mulsd, divsd | + − × ÷ |
sqrtsd xmm0, xmm1 | Quadratwurzel |
cvtsi2sd xmm0, rax | Ganzzahl → double |
cvttsd2si rax, xmm0 | double → Ganzzahl (abschneiden) |
comisd xmm0, xmm1 | Vergleich (setzt ZF/CF, dann ja/jb) |
section .data
a dq 3.5
b dq 2.0
hun dq 100.0
section .bss
puffer resb 32
section .text
global _start
_start:
movsd xmm0, [a]
addsd xmm0, [b] ; 3.5 + 2.0 = 5.5
mulsd xmm0, [hun] ; * 100 = 550
cvttsd2si rax, xmm0
call ausgabe_zahl
movsd xmm0, [a]
mulsd xmm0, xmm0 ; 3.5² = 12.25
mulsd xmm0, [hun]
cvttsd2si rax, xmm0 ; 1225 (zwei Nachkommastellen als Ganzzahl)
call ausgabe_zahl
mov rax, 2
cvtsi2sd xmm1, rax
sqrtsd xmm1, xmm1 ; sqrt(2)
movsd xmm2, [hun]
mulsd xmm2, xmm2 ; 10000
mulsd xmm1, xmm2
cvttsd2si rax, xmm1 ; 14142 → sqrt(2) = 1.4142
call ausgabe_zahl
movsd xmm0, [a]
movsd xmm1, [b]
comisd xmm0, xmm1
ja .groesser
xor rax, rax
jmp .ausgeben
.groesser:
mov rax, 1 ; 3.5 > 2.0
.ausgeben:
call ausgabe_zahl
mov rax, 60
xor rdi, rdi
syscall
; --- Hilfsroutine: gibt die Zahl in RAX dezimal aus (mit Zeilenumbruch), alle Register bleiben erhalten ---
ausgabe_zahl:
push rax
push rbx
push rcx
push rdx
push rsi
push rdi
mov rcx, puffer + 20 ; Ende des Puffers
mov byte [rcx], 10 ; Zeilenumbruch ans Ende
mov rbx, 10
.schleife:
xor rdx, rdx
div rbx ; RAX = RAX / 10, RDX = Rest
add dl, '0' ; Ziffer in ASCII umwandeln
dec rcx
mov [rcx], dl
test rax, rax
jnz .schleife
mov rax, 1 ; write
mov rdi, 1
mov rsi, rcx
lea rdx, [puffer + 21]
sub rdx, rcx
syscall
pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
ret550 1225 14142 1
SIMD: mehrere Werte mit einem Befehl
SIMD (Single Instruction, Multiple Data) bearbeitet mehrere Werte gleichzeitig. Ein XMM-Register fasst z. B. vier float, zwei double oder vier 32-Bit-Ganzzahlen. Befehle mit ps/pd (packed) wirken auf alle Teile, padd* auf Ganzzahlen. Neuere Erweiterungen: AVX/AVX2 (256 Bit, ymm), AVX-512 (zmm).
section .data
v1 dd 1, 2, 3, 4 ; vier 32-Bit-Ganzzahlen
v2 dd 10, 20, 30, 40
section .bss
puffer resb 32
erg resd 4
section .text
global _start
_start:
movdqu xmm0, [v1]
movdqu xmm1, [v2]
paddd xmm0, xmm1 ; vier Additionen auf einmal
movdqu [erg], xmm0
mov eax, [erg]
call ausgabe_zahl ; 11
mov eax, [erg + 4]
call ausgabe_zahl ; 22
mov eax, [erg + 8]
call ausgabe_zahl ; 33
mov eax, [erg + 12]
call ausgabe_zahl ; 44
movdqu xmm0, [v2]
pslld xmm0, 1 ; alle vier Werte verdoppeln (Linksschieben)
movdqu [erg], xmm0
mov eax, [erg + 8]
call ausgabe_zahl ; 60
mov rax, 60
xor rdi, rdi
syscall
; --- Hilfsroutine: gibt die Zahl in RAX dezimal aus (mit Zeilenumbruch), alle Register bleiben erhalten ---
ausgabe_zahl:
push rax
push rbx
push rcx
push rdx
push rsi
push rdi
mov rcx, puffer + 20 ; Ende des Puffers
mov byte [rcx], 10 ; Zeilenumbruch ans Ende
mov rbx, 10
.schleife:
xor rdx, rdx
div rbx ; RAX = RAX / 10, RDX = Rest
add dl, '0' ; Ziffer in ASCII umwandeln
dec rcx
mov [rcx], dl
test rax, rax
jnz .schleife
mov rax, 1 ; write
mov rdi, 1
mov rsi, rcx
lea rdx, [puffer + 21]
sub rdx, rcx
syscall
pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
ret11 22 33 44 60
Compiler nutzen SIMD automatisch (Auto-Vektorisierung, gcc -O3 -march=native). Für Bilder, Audio, Spiele, Kryptografie und Machine Learning bringt es oft das 4- bis 16-fache Tempo.
Andere Architekturen
x86-64 ist nicht allein. Das Prinzip bleibt gleich: Register, Speicher, Sprünge, Systemaufrufe.
| Architektur | Einsatz | Beispielbefehle |
|---|---|---|
| ARM64 (AArch64) | Smartphones, Apple Silicon, Server | mov x0, #5, add x0, x0, x1, ldr, str, bl, ret |
| RISC-V | offen, Forschung, Embedded | li a0, 5, add a0, a0, a1, lw, sw, jal |
| x86 (32 Bit) | Altsysteme | mov eax, 1, int 0x80 |
| AVR / ARM Cortex-M | Mikrocontroller (Arduino, STM32) | direkter Zugriff auf Pins |
Ein Vergleich: Dieselbe Addition a + b.
x86-64: mov rax, rdi ; ARM64: add x0, x0, x1 ; RISC-V: add a0, a0, a1
add rax, rsi ; ret ; ret
retARM und RISC-V sind Load/Store-Architekturen: Nur ldr/str bzw. lw/sw greifen auf Speicher zu, Rechenbefehle arbeiten ausschließlich mit Registern.
Merke
- Fließkommazahlen rechnen mit XMM-Registern und SSE2 (
addsd,mulsd,sqrtsd) - Umwandlung:
cvtsi2sd(int → double),cvttsd2si(double → int) - SIMD (
paddd,addps, AVX) bearbeitet mehrere Werte pro Befehl - Andere Architekturen (ARM64, RISC-V) folgen demselben Prinzip mit anderen Befehlen
Aufgabe
Berechne den Mittelwert von 3, 4 und 8 als double und gib ihn mit zwei Nachkommastellen als Ganzzahl (mal 100) aus.