Kurzusok
/
x86-64 Assembly
x86-64 Assembly
/
Feladatok
/
Szabadúszói díjak
Szabadúszói díjak

Szabadúszói díjak

Tanulófeladat

Bevezetés

Lebegőpontos számok

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.

XMM-regiszterek

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.

Számok közötti átalakítás

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ő:

  • A xx a forrásoperandus típusának helyőrzője.
  • A 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.

Kerekítés

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.

Aritmetikai utasítások

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)

Mozgatás

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

Összehasonlítások

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'

Hívási konvenció

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.

Memória

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

Utasítások

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:

  • A napi díj az óradíj nyolcszorosa.
  • Egy hónapban 22 számlázható nap van.

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%).

Note

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

1. Számítsd ki a napi díjat egy óradíj alapján

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.

2. Számítsd ki a kedvezményes árat

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.

3. Számítsd ki a havi díjat egy óradíj és egy kedvezmény alapján

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.

4. Számítsd ki a teljes munkanapok számát egy költségvetés, egy óradíj és egy kedvezmény alapján

Implementálj egy days_in_budget függvényt, amely a következő paramétereket kapja:

  1. Egy költségvetést, 64 bites előjel nélküli egész számként.
  2. Egy óradíjat, 64 bites lebegőpontos számként.
  3. Egy kedvezményt százalékban, 64 bites lebegőpontos számként.

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.

Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg
x86-64 Assembly Exercism

Készen állsz elkezdeni a(z) Szabadúszói díjak feladatot?

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.