Track
/
x86-64 Assembly
x86-64 Assembly
/
Esercizi
/
Tariffe da freelance
Tariffe da freelance

Tariffe da freelance

Esercizio di apprendimento

Introduzione

Numeri in virgola mobile

Un numero in virgola mobile è un numero che può avere una parte frazionaria, come -1.23 o 3.1415.

Per rappresentare un tale numero come una sequenza di byte, x86-64 usa lo standard IEEE-754. I due formati più comuni in questo standard sono i numeri in virgola mobile a precisione singola e a precisione doppia. x86-64 supporta entrambi.

Un numero in virgola mobile a precisione singola è largo 32 bit, mentre uno a precisione doppia è largo 64 bit.

Registri XMM

In x86-64, ci sono 16 registri dedicati per eseguire aritmetica in virgola mobile, chiamati xmm0 fino a xmm15.

Quei registri sono larghi almeno 128 bit, ma i processori recenti aumentano questa larghezza a 256 bit o 512 bit. Tuttavia, in pratica, la maggior parte dei numeri in virgola mobile usa solo i 32 bit o i 64 bit più bassi, a seconda della precisione desiderata.

Conversione tra numeri

Esiste una famiglia di istruzioni per convertire tra formati numerici: cvtxx2yy.

Sia xx che yy non sono letterali, ma segnaposto:

  • xx è un segnaposto per il tipo dell'operando sorgente.
  • yy è un segnaposto per il tipo dell'operando destinazione.

Questo segnaposto può assumere molte forme, tra cui:

segnaposto significato
si intero con segno
ss numero in virgola mobile a 32 bit
sd numero in virgola mobile a 64 bit

Per esempio:

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

Nota che è possibile convertire un intero a 32 bit in un numero in virgola mobile a 64 bit o un intero a 64 bit in un numero in virgola mobile a 32 bit. Allo stesso modo, è anche possibile convertire un numero in virgola mobile a 32 bit in un intero a 64 bit o un numero in virgola mobile a 64 bit in un intero a 32 bit. In altre parole, le dimensioni di entrambi gli operandi in un cvtxx2yy non devono essere le stesse:

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

Quando il numero in virgola mobile non può essere espresso esattamente come un intero, cvtxx2si arrotonda il numero secondo il controllo di arrotondamento in un registro speciale chiamato mxcsr. Questo può essere un arrotondamento al più vicino, verso il basso, verso l'alto o una troncatura. Non c'è un valore predefinito garantito all'ingresso della funzione.

Nota che un risultato esatto non viene arrotondato e, quindi, non è influenzato dal controllo di arrotondamento. Ad esempio, 42.0 verrebbe sempre convertito in 42.

Esiste una famiglia simile di istruzioni con il formato cvttxx2si (con una t in più) che tronca sempre il risultato.

Arrotondamento

La famiglia di istruzioni roundxx converte un numero in virgola mobile nell'operando sorgente in un numero in virgola mobile arrotondato memorizzato nell'operando destinazione.

Qui, xx è un segnaposto per il tipo degli operandi, come al solito: ss per i numeri in virgola mobile a precisione singola e sd per quelli a precisione doppia.

Queste istruzioni prendono un numero costante come terzo operando. Questo numero indica la modalità di arrotondamento:

Numero Modalità
0 Al più vicino
1 Arrotonda verso il basso
2 Arrotonda verso l'alto
3 Tronca

Per esempio:

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

Poiché queste istruzioni producono un risultato esatto, possono essere usate in preparazione a un cvtxx2si.

Istruzioni aritmetiche

Molte operazioni con numeri in virgola mobile usano istruzioni con un nome simile a quello del loro equivalente per gli interi, ma aggiungendo il segnaposto necessario (ss o sd) alla fine. Queste istruzioni sono a due operandi, con la solita semantica, anche quelle che eseguono moltiplicazione o divisione:

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)

Spostamento

Le istruzioni per spostare da un registro XMM a un altro sono movss o 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

Non è possibile spostare un immediato (un intero costante con segno) direttamente in un registro xmm. Questo numero deve prima essere spostato in un registro di uso generale e poi convertito usando cvtsi2xx:

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

Confronti

Esistono due gruppi di istruzioni per fare un confronto tra due numeri in virgola mobile. Prendono la forma comixx e ucomixx, dove xx è ss o sd. Entrambi impostano i flag in rflags come al solito, seguendo la stessa semantica di cmp.

La differenza tra loro è che ucomixx imposta il parity flag (PF) se uno dei due numeri in virgola mobile ha un valore NaN, mentre comixx solleva un'eccezione in questo caso.

Il confronto tra valori in virgola mobile è senza segno:

ucomisd xmm0, xmm1
ja .greater         ; 'ja' must be used for "greater", not 'jg'
jb .lesser          ; 'jb' must be used for "lesser", not 'jl'

Convenzione di chiamata

Nella System V ABI, i primi otto argomenti in virgola mobile vengono passati alle funzioni in ordine, da xmm0 a xmm7. I valori in virgola mobile vengono restituiti dalle funzioni in xmm0 e, se necessario, in xmm1.

Tutti i registri xmm sono liberamente utilizzabili, nessuno di essi è riservato al chiamante.

Memoria

I valori in virgola mobile possono essere definiti in memoria seguendo la stessa semantica degli interi della stessa dimensione. NASM (The Netwide Assembler, l'assembler usato da questo track) permette anche la dichiarazione di valori in virgola mobile in notazione scientifica.

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

Istruzioni

In questo esercizio scriverai del codice per aiutare un freelancer a comunicare con un project manager. Il tuo compito è fornire alcune funzioni di utilità per calcolare rapidamente le tariffe giornaliere e mensili, facoltativamente con uno sconto dato.

Stabiliamo innanzitutto alcune regole tra il freelancer ed il project manager:

  • La tariffa giornaliera è 8 volte la tariffa oraria.
  • Un mese ha 22 giorni fatturabili.

A volte il freelancer propone di applicare uno sconto sulla propria tariffa giornaliera (per esempio per i clienti più fedeli o per i clienti senza scopo di lucro).

Gli sconti sono rappresentati come numeri frazionari che esprimono percentuali, per esempio 25.0 (25%).

Note

Questi sono i suffissi delle istruzioni in virgola mobile:

Suffisso Dimensione dell'operando
ss 32 bit
sd 64 bit

Queste sono alcune delle istruzioni menzionate in questo concetto:

Istruzione Descrizione
movss/movsd a, b copia b in a
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 converte l'intero b nel numero in virgola mobile a
roundss/roundsd a, b, c arrotonda il numero in virgola mobile b secondo c e lo memorizza in a
cvtss2si/cvtsd2si a, b converte il numero in virgola mobile b nell'intero a
comiss/comisd a, b fa un confronto tra a e b, solleva un'eccezione se NaN
ucomiss/ucomisd a, b fa un confronto tra a e b, imposta PF se NaN

Questi sono i numeri di controllo dell'arrotondamento in roundss e roundsd:

Numero Modalità
0 Al più vicino
1 Per difetto (down)
2 Per eccesso (up)
3 Troncamento

1. Calcola la tariffa giornaliera data una tariffa oraria

Implementa una funzione chiamata daily_rate per calcolare la tariffa giornaliera data come parametro una tariffa oraria, come numero in virgola mobile a 64 bit. Il contratto stabilisce che un giorno ha 8 ore fatturabili.

daily_rate(60)
// => 480.0

La tariffa giornaliera restituita deve essere un numero in virgola mobile a 64 bit.

2. Calcola un prezzo scontato

Implementa una funzione apply_discount per calcolare il prezzo dopo uno sconto. Deve accettare due parametri: il prezzo originale e la percentuale di sconto, entrambi come numeri in virgola mobile a 64 bit.

apply_discount(150.0, 10.0)
// => 135.0

Il valore restituito deve essere sempre un numero in virgola mobile a 64 bit.

3. Calcola la tariffa mensile, data una tariffa oraria e uno sconto

Implementa una funzione monthly_rate per calcolare la tariffa mensile scontata. Prende due parametri, una tariffa oraria e lo sconto in percentuale, entrambi come numeri in virgola mobile a 64 bit.

monthly_rate(77.0, 10.5)
// => 12130

La tariffa mensile restituita deve essere arrotondata per eccesso all'intero a 64 bit più vicino.

4. Calcola il numero di giornate lavorative complete dati un budget, una tariffa oraria ed uno sconto

Implementa una funzione days_in_budget che prende come parametri:

  1. Un budget, come intero senza segno a 64 bit.
  2. Una tariffa oraria, come numero in virgola mobile a 64 bit.
  3. Uno sconto in percentuale, come numero in virgola mobile a 64 bit.

Questa funzione deve restituire il numero di giornate lavorative complete coperte dal budget.

days_in_budget(20000, 80, 11.0)
// => 35

Il numero di giorni restituito deve essere arrotondato per difetto all'intero a 32 bit più vicino.

Modifica tramite GitHub Il link si apre in una nuova finestra o scheda
x86-64 Assembly Exercism

Vuoi iniziare Tariffe da freelance?

Iscriviti a Exercism per imparare e padroneggiare x86-64 Assembly con 22 concetti130 esercizi e il mentoring di persone reali, tutto gratis.