A lebegőpontos szám olyan szám, amelynek törtrésze is lehet, például -1.23 vagy 3.1415.
Ahhoz, hogy egy ilyen számot bájtsorozatként ábrázoljon, az x86-64 az IEEE-754 szabványt használja. Ebben a szabványban a két leggyakoribb formátum az egyszeres pontosságú és a kétszeres pontosságú lebegőpontos szám. Az x86-64 mindkettőt támogatja.
Az egyszeres pontosságú lebegőpontos szám 32 bites, a kétszeres pontosságú pedig 64 bites.
Az x86-64-ben 16 dedikált regiszter végzi a lebegőpontos aritmetikát, ezek neve xmm0-tól xmm15-ig terjed.
Ezek a regiszterek legalább 128 bitesek, az újabb processzorokban azonban ez 256 vagy 512 bitre nő. A gyakorlatban viszont a legtöbb lebegőpontos szám csak a legalsó 32 vagy 64 bitet használja, a kívánt pontosságtól függően.
Létezik egy utasításcsalád a számformátumok közötti átalakításra: cvtxx2yy.
A xx és a yy sem nem literál, hanem helyőrző:
xx a forrásoperandus típusának helyőrzője.yy a céloperandus típusának helyőrzője.Ez a helyőrző sokféle alakot ölthet, többek között:
| helyőrző | jelentés |
|---|---|
si |
előjeles egész szám |
ss |
32 bites lebegőpontos szám |
sd |
64 bites lebegőpontos szám |
Például:
cvtsi2ss xmm0, eax ; converts a 32-bit signed integer to a single-precision floating-point
cvtsd2si rax, xmm1 ; converts a double-precision floating-point to a 64-bit signed integer
Vedd észre, hogy lehetséges 32 bites egész számot 64 bites lebegőpontos számmá alakítani, vagy 64 bites egész számot 32 bites lebegőpontossá.
Hasonlóképpen az is lehetséges, hogy egy 32 bites lebegőpontos számot 64 bites egésszé, vagy egy 64 bites lebegőpontos számot 32 bites egésszé alakíts.
Más szóval, a cvtxx2yy két operandusának mérete nem kell, hogy ugyanaz legyen:
cvtsi2sd xmm0, eax ; converts a 32-bit signed integer to a double-precision floating-point
cvtsi2ss xmm0, rax ; converts a 64-bit signed integer to a single-precision floating-point
cvtss2si rax, xmm0 ; converts a single-precision floating-point to a 64-bit signed integer
cvtsd2si eax, xmm0 ; converts a double-precision floating-point to a 32-bit signed integer
Amikor a lebegőpontos szám nem fejezhető ki pontosan egész számként, a cvtxx2si a számot az mxcsr nevű speciális regiszterben lévő kerekítési vezérlés szerint kerekíti.
Ez lehet kerekítés a legközelebbi értékre, lefelé kerekítés, felfelé kerekítés vagy csonkítás.
A függvény belépési pontján nincs garantált alapértelmezés.
Vedd észre, hogy a pontos eredményt nem kerekíti, így a kerekítési vezérlés sem hat rá.
Például a 42.0 mindig 42-vé alakul.
Létezik egy hasonló utasításcsalád cvttxx2si formátumban (egy plusz t-vel), amely mindig csonkítja az eredményt.
A roundxx utasításcsalád a forrásoperandusban lévő lebegőpontos számot kerekített lebegőpontos számmá alakítja, amelyet a céloperandusban tárol.
Itt a xx a szokásos módon az operandusok típusának helyőrzője: ss az egyszeres pontosságú, sd a kétszeres pontosságú lebegőpontos számokhoz.
Ezek az utasítások egy konstans számot vesznek harmadik operandusként. Ez a szám a kerekítési módot jelöli:
| Szám | Mód |
|---|---|
| 0 | Legközelebbi |
| 1 | Lefelé (floor) |
| 2 | Felfelé (ceil) |
| 3 | Csonkítás |
Például:
roundss xmm0, xmm1, 1 ; this rounds down (takes the floor) a 32-bit floating-point in xmm1 and stores it in xmm0
roundsd xmm2, xmm2, 2 ; this rounds up (takes the ceil) a 64-bit floating-point in xmm2 and stores it in xmm2
Mivel ezek az utasítások pontos eredményt adnak, előkészületként használhatók egy cvtxx2si utasításhoz.
Sok lebegőpontos művelet olyan utasítást használ, amelynek a neve hasonló az egész számoknál megszokott megfelelőjéhez, csak a végére kerül a szükséges helyőrző (ss vagy sd).
Ezek az utasítások kétoperandusúak, a szokásos szemantikával, még azok is, amelyek szorzást vagy osztást végeznek:
addss xmm1, xmm2 ; xmm1 = xmm1 + xmm2 (all 32-bit floating-point)
mulsd xmm4, xmm5 ; xmm4 = xmm4 * xmm5 (all 64-bit floating-point)
divss xmm0, xmm3 ; xmm0 = xmm0 / xmm3 (all 32-bit floating-point)
Az egyik XMM-regiszterből a másikba mozgatás utasításai a movss és a movsd:
movss xmm1, xmm0 ; this copies a 32-bit floating-point from xmm0 to xmm1
movsd xmm6, xmm4 ; this copies a 64-bit floating-point from xmm4 to xmm6
Nem lehet közvetlenül egy immediate értéket (konstans előjeles egész számot) xmm-regiszterbe mozgatni.
Ezt a számot először egy általános célú regiszterbe kell mozgatni, majd a cvtsi2xx segítségével átalakítani:
mov rax, 10 ; rax is a 64-bit integer with value 10
cvtsi2sd xmm0, rax ; xmm0 is a 64-bit floating-point with value 10.0
Két utasításcsoport létezik két lebegőpontos szám összehasonlítására.
Formájuk comixx és ucomixx, ahol a xx vagy ss, vagy sd.
Mindkettő a szokásos módon állítja a rflags jelzőit, ugyanazzal a szemantikával, mint a cmp.
A kettő közötti különbség, hogy a ucomixx beállítja a parity flag (PF)-et, ha a két lebegőpontos szám valamelyike NaN értékű, a comixx viszont ebben az esetben kivételt vált ki.
A lebegőpontos értékek közötti összehasonlítás előjel nélküli:
ucomisd xmm0, xmm1
ja .greater ; 'ja' must be used for "greater", not 'jg'
jb .lesser ; 'jb' must be used for "lesser", not 'jl'
A System V ABI-ban az első nyolc lebegőpontos argumentum sorrendben, xmm0-tól xmm7-ig kerül átadásra a függvényeknek.
A lebegőpontos értékeket a függvények xmm0-ban, szükség esetén xmm1-ben adják vissza.
Az összes xmm regiszter szabadon használható, egyik sincs a hívó számára fenntartva.
A lebegőpontos értékek ugyanazzal a szemantikával definiálhatók a memóriában, mint az azonos méretű egész számok. A NASM (The Netwide Assembler, az ezen a kurzuson használt assembler) a tudományos jelölésmódot is engedélyezi a lebegőpontos értékek deklarálásához.
section .data
pi dq 3.141592653589793 ; this is a double-precision floating-point value
exp_num dd 1.e5 ; this is a single precision floating-point value equal to 1 * 10⁵
exp_num2 dd 1.e+5 ; this is a single precision floating-point value equal to 1 * 10⁵
frac dq 1.e-10 ; this is a double precision floating-point value equal to 1 * 10⁻¹⁰, i.e., 0.0000000001
Ebben a feladatban olyan kódot írsz majd, amely segít egy szabadúszónak kommunikálni egy projektmenedzserrel. A feladatod, hogy néhány segédeszközt biztosíts a napi és havi díjak gyors kiszámításához, opcionálisan egy megadott kedvezménnyel.
Először néhány szabályt rögzítünk a szabadúszó és a projektmenedzser között:
Néha a szabadúszó kedvezményt ajánl a napi díjára (például a leghűségesebb ügyfeleinek vagy nonprofit ügyfeleknek).
A kedvezményeket százalékokat reprezentáló törtszámokként modellezzük, például 25.0 (25%).
Ezek a lebegőpontos utasítások utótagjai:
| Utótag | Operandusméret |
|---|---|
ss |
32 bites |
sd |
64 bites |
Ezek néhány az ebben a fogalomban említett utasítás közül:
| Utasítás | Leírás |
|---|---|
| movss/movsd a, b | b-t az a-ba másolja |
| addss/addsd a, b | a = a + b |
| subss/subsd a, b | a = a - b |
| mulss/mulsd a, b | a = a * b |
| divss/divsd a, b | a = a / b |
| cvtsi2ss/cvtsi2sd a, b | az egész b-t lebegőpontos a-vá alakítja |
| roundss/roundsd a, b, c | a lebegőpontos b-t c szerint kerekíti, és az a-ban tárolja |
| cvtss2si/cvtsd2si a, b | a lebegőpontos b-t egész a-vá alakítja |
| comiss/comisd a, b | összehasonlítást végez a és b között, kivétel, ha NaN
|
| ucomiss/ucomisd a, b | összehasonlítást végez a és b között, beállítja a PF jelzőt, ha NaN
|
Ezek a kerekítést vezérlő számok a roundss és roundsd utasításban:
| Szám | Mód |
|---|---|
| 0 | Legközelebbihez kerekítés |
| 1 | Lefelé kerekítés |
| 2 | Felfelé kerekítés |
| 3 | Csonkítás |
Implementálj egy daily_rate nevű függvényt, amely kiszámítja a paraméterként kapott óradíjból a napi díjat, 64 bites lebegőpontos számként.
A szerződés szerint egy nap 8 számlázható órából áll.
daily_rate(60)
// => 480.0
A visszaadott napi díjnak 64 bites lebegőpontos számnak kell lennie.
Implementálj egy apply_discount függvényt, amely kiszámítja a kedvezmény utáni árat.
Két paramétert kell fogadnia: az eredeti árat és a kedvezmény mértékét százalékban, mindkettőt 64 bites lebegőpontos számként.
apply_discount(150.0, 10.0)
// => 135.0
A visszaadott értéknek mindig 64 bites lebegőpontos számnak kell lennie.
Implementálj egy monthly_rate függvényt, amely kiszámítja a kedvezményes havi díjat.
Két paramétert kap: egy óradíjat és a kedvezményt százalékban, mindkettőt 64 bites lebegőpontos számként.
monthly_rate(77.0, 10.5)
// => 12130
A visszaadott havi díjat felfelé kell kerekíteni (a felső egészre), a legközelebbi 64 bites egész számra.
Implementálj egy days_in_budget függvényt, amely a következő paramétereket kapja:
A függvény adja vissza a költségvetésből fedezhető teljes munkanapok számát.
days_in_budget(20000, 80, 11.0)
// => 35
A visszaadott napok számát lefelé kell kerekíteni (az alsó egészre), a legközelebbi 32 bites egész számra.
Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) x86-64 Assembly nyelvet 22 fogalom130 feladat segítségével, valódi emberi mentorálással, mindez ingyen.