Laboratori 5: Coma flotant i compilació separada

Lliuraments

Cal lliurar les respostes als exercicis següents al campus virtual abans de l’inici de la sessió de laboratori:

Exercici Nom i format del fitxer
Exercici 32.1 s5_1_1.s i s5_1_2.s
Exercici 32.2 s5_2_1.md i s5_2_1.s
Exercici 32.3 s5_3_1.s
Exercici 32.4 s5_4_1.md

Lectura prèvia

Abans de fer la sessió, cal haver llegit i entès els continguts següents:

Concepte On trobar-ho
Format IEEE 754 de precisió simple: signe, exponent esbiaixat, fracció Secció 5.1.3, RISC-V 5.1
El bit ocult Essencial 5.1
Codificació i decodificació de nombres en coma flotant Exemple 5.2, Exemple 5.3
Modes d’arrodoniment Secció 5.1.7
Desplaçaments lògics i aritmètics (sll, srl, sra, slli, srli, srai) RISC-V 3.1, RISC-V 3.2
Operacions lògiques bit a bit i de comparació (and, or, slt) RISC-V 3.3, RISC-V 3.5
Subrutines: pas de paràmetres i retorn de resultats Secció 3.9.2, RISC-V 3.17
Subrutines multinivell: registres segurs i bloc d’activació Secció 3.9.4, RISC-V 3.22
Compilació separada i directiva .globl Secció 3.10, RISC-V 3.24

32.1 Compilació separada a RARS

En un projecte real, el codi es divideix en mòduls independents que es compilen i assemblen per separat (Secció 3.10). Cadascun viu en el seu propi fitxer font, declara com a globals les etiquetes que han de ser visibles des d’altres mòduls, i referencia les etiquetes externes sense necessitat de conèixer-ne els detalls interns.

RARS reprodueix aquest flux de manera directa: amb l’opció Settings → Assemble all files currently open activada, l’assemblador processa tots els fitxers oberts alhora i resol automàticament les referències entre ells, assumint la funció combinada d’assemblador i enllaçador.

Essencial 32.1: Compilació separada — directiva .globl

Qualsevol etiqueta referenciada des d’un altre fitxer ha de declarar-se amb .globl al fitxer on es defineix. Sense aquesta declaració, l’etiqueta és invisible per als altres mòduls i RARS finalitza amb un error de símbol no resolt. Només s’hi declaren els símbols que altres mòduls necessiten: el programa principal no en porta, perquè cap fitxer no hi fa referència i RARS no el cerca per nom (vegeu l’aprofundiment següent).

ExempleComprovació pràctica

Un cop oberts tots els fitxers i activada l’opció Assemble all files currently open, assembleu el projecte. Al panell Labels (o Symbol Table) de RARS hi apareixeran totes les etiquetes globals dels mòduls. Comproveu que les etiquetes esperades hi són i que les seves adreces s’han resolt correctament.

RISC-V 32.1: RARS — Ordre d’assemblatge de diversos fitxers

L’execució comença a la primera instrucció del segment .text (Secció 2.3.1). Quan s’assemblen diversos fitxers alhora, aquell .text és el combinat, i l’ordre en què s’hi encadenen els mòduls el decideix RARS: amb Assemble all files currently open, el primer que s’assembla és el del fitxer que té la pestanya activa en prémer F3.

Per tant, la pestanya activa ha de ser la del fitxer que conté el programa principal. Si és la d’un fitxer de subrutines, el programa arrenca dins d’una subrutina.

RARS no té enllaçador separat: assembla i resol les referències entre mòduls en una sola passada, de manera que el que es tria aquí és l’ordre d’assemblatge (el procés que ho fa en dues fases és a Secció 3.10.4).

⚠️ L’assemblatge no falla si l’ordre és el dolent. La consola diu operation completed successfully en tots dos casos, i l’error només apareix en executar:

Assemble: assembling s5_1_2.s, s5_1_1.s
Assemble: operation completed successfully.
Assemble: assembling s5_1_1.s, s5_1_2.s
Assemble: operation completed successfully.

L’execució, en canvi, sí que els distingeix:

-- program is finished running (0) --
Go: running s5_1_1.s

Error in : Instruction load access error

Go: execution terminated with errors.

La primera pista és el nom del fitxer: Go: running s5_1_1.s diu que RARS ha començat pel fitxer de la subrutina, i ho diu abans del missatge d’error. Si el programa s’atura amb un error d’accés a instrucció just en començar, cal mirar aquella línia abans que res.

A la solució de l’exercici hi ha els dos segments de text comparats instrucció per instrucció, per a qui vulgui veure què hi canvia.


Exercici 32.1 (s5_1_1.s i s5_1_2.s) Traduïu a RV32I el programa C del Codi 32.1, repartint el codi en dos fitxers tal com s’indica:

  • s5_1_1.s: conté únicament la subrutina abs.
  • s5_1_2.s: conté el programa principal, que crida abs i escriu el resultat a resultat.
Codi 32.1: Subrutina abs i programa principal.
abs.c
int resultat;

int abs(int x) {
    if (x < 0)
        return -x;
    else
        return x;
}

void main() {
    resultat = abs(-7);
}

Tingueu en compte:

  • abs és uninivell: determineu si cal algun registre segur.
  • El programa principal és multinivell: determineu quins valors, si n’hi ha, travessen la crida i cal preservar.
  • Obriu els dos fitxers a RARS, activeu Settings → Assemble all files currently open i assembleu. Comproveu que abs apareix al panell Labels.

Comproveu que, en acabar, la variable global resultat conté 7, i que conté 0 si el programa principal crida abs(0).


Solució 32.1. Anàlisi de registres segurs.

abs és uninivell: no fa cap crida, de manera que no hi ha cap registre en risc de ser destruït; tots els temporals van a registres t. BA buit.

El programa principal és multinivell (crida abs), però no necessita cap registre segur ni bloc d’activació: no hi ha cap valor generat abans de la crida que s’usi després (el resultat d’abs s’usa immediatament en tornar), i com que és el punt d’entrada i mai no torna a cap invocant (acaba amb ecall), tampoc cal preservar ra. BA buit.

s5_1_1.s
.globl abs

.text

# abs(int x)
# a0 = x; retorna |x| a a0
abs:
        bge     a0, zero, positiu  # si x >= 0, retorna x directament
        neg     a0, a0             # a0 = -x
positiu:
        ret
s5_1_2.s
.data
resultat: .word 0

.text

        li      a0, -7
        jal     ra, abs         # a0 = abs(-7) = 7

        la      t0, resultat
        sw      a0, 0(t0)       # resultat = 7

        li      a7, 93
        li      a0, 0
        ecall
ExempleComprovació pràctica

Obriu s5_1_1.s i s5_1_2.s a RARS, activeu Settings → Assemble all files currently open i assembleu. Al panell Labels hi ha d’aparèixer abs amb la seva adreça: és l’únic símbol declarat .globl, i per això és l’únic visible entre mòduls. Amb abs(0), el salt bge es pren i es retorna el valor directament.

L’ordre d’assemblatge, vist al segment de text. Feu File → Dump Memory… (Ctrl-D) amb el segment .text i el format Text/Data Segment Window: escriu en un fitxer el llistat del segment de text, i permet comparar els dos ordres de la pestanya activa (RISC-V 32.1). Per línia d’ordres, és dump .text SegmentWindow <fitxer>:

Correcte — pestanya activa: s5_1_2.s

Address     Code        Basic                    Line  Source
0x00400000  0xff900513  addi x10,x0,0xfffffff9    6    li   a0, -7
0x00400004  0x01c000ef  jal x1,0x0000001c         7    jal  ra, abs
0x00400008  0x0fc10297  auipc x5,0x0000fc10       9    la   t0, resultat
0x0040000c  0xff828293  addi x5,x5,0xfffffff8
0x00400010  0x00a2a023  sw x10,0(x5)             10    sw   a0, 0(t0)
0x00400014  0x05d00893  addi x17,x0,0x0000005d   12    li   a7, 93
0x00400018  0x00000513  addi x10,x0,0            13    li   a0, 0
0x0040001c  0x00000073  ecall                    14    ecall
0x00400020  0x00055463  bge x10,x0,0x00000008     8    bge  a0, zero, positiu
0x00400024  0x40a00533  sub x10,x0,x10            9    neg  a0, a0
0x00400028  0x00008067  jalr x0,x1,0             11    ret

Incorrecte — pestanya activa: s5_1_1.s

Address     Code        Basic                    Line  Source
0x00400000  0x00055463  bge x10,x0,0x00000008     8    bge  a0, zero, positiu
0x00400004  0x40a00533  sub x10,x0,x10            9    neg  a0, a0
0x00400008  0x00008067  jalr x0,x1,0             11    ret
0x0040000c  0xff900513  addi x10,x0,0xfffffff9    6    li   a0, -7
0x00400010  0xff1ff0ef  jal x1,0xfffffff0         7    jal  ra, abs
0x00400014  0x0fc10297  auipc x5,0x0000fc10       9    la   t0, resultat
0x00400018  0xfec28293  addi x5,x5,0xffffffec
0x0040001c  0x00a2a023  sw x10,0(x5)             10    sw   a0, 0(t0)
0x00400020  0x05d00893  addi x17,x0,0x0000005d   12    li   a7, 93
0x00400024  0x00000513  addi x10,x0,0            13    li   a0, 0
0x00400028  0x00000073  ecall                    14    ecall

Dues coses que el bolcat no diu tot sol:

  1. Amb l’ordre incorrecte, la primera instrucció de .text és de la subrutina abs (bge a0, zero, positiu). És allà on RARS posa el PC, de manera que el programa comença per la primera instrucció d’abs, amb a0 sense inicialitzar.
  2. La mateixa instrucció font canvia de codificació. jal ra, abs és 0x01c000ef en un cas i 0xff1ff0ef en l’altre —un salt endavant i un enrere—, i el la t0, resultat passa d’addi x5,x5,-8 a addi x5,x5,-20 per arribar a la mateixa adreça. Són desplaçaments relatius al PC, corregits segons on ha quedat cada mòdul: és la correcció d’adreces de Secció 3.10.4, aquí visible en dos bolcats.

32.2 Subrutina descompon

En aquesta sessió es desenvolupa un programa que converteix un nombre real codificat en coma fixa al format de coma flotant IEEE 754 de precisió simple (Secció 5.1.3). El programa s’estructura en dues subrutines (descompon i compon) i un programa principal repartits en dos fitxers, aplicant la compilació separada de la secció anterior.

El format de coma fixa que s’usa en aquesta sessió té 32 bits: el bit 31 conté el signe (0 per als positius) i els bits 30–0 representen la magnitud en base 2, amb 19 bits de part entera (bits 30–12) i 12 bits de part fraccionària (bits 11–0):

\[\text{valor}(A) = (-1)^{A_{31}} \times \sum_{i=0}^{30} A_i \cdot 2^{i-12}\]

El format de coma flotant IEEE 754 de precisió simple (RISC-V 5.1) té 32 bits: 1 bit de signe (bit 31), 8 bits d’exponent esbiaixat amb biaix 127 (bits 30–23) i 23 bits de fracció (bits 22–0), amb bit ocult implícit (Essencial 5.1).

El programa de conversió s’organitza en dos passos:

main.c
int signe;
int exponent;
int mantissa;
int cfixa    = 0x87D18A00;
int cflotant;

void main() {
    descompon(cfixa, &signe, &exponent, &mantissa);
    cflotant = compon(signe, exponent, mantissa);
}

cflotant es declara com a int, no float: el valor que es construeix és el patró de bits de la representació IEEE 754, no un nombre que el compilador hagi de convertir. Si es declarés float, l’assignació convertiria el valor enter a coma flotant en lloc de reinterpretar-ne els bits.

La subrutina descompon extreu de cfixa els tres camps que necessita IEEE 754: el signe, la fracció (els 23 bits que segueixen el bit ocult de la mantissa normalitzada) i l’exponent esbiaixat amb biaix 127 (8 bits):

Codi 32.2: Descomposició d’un nombre de coma fixa en els camps d’IEEE 754.
descompon.c
void descompon(int cf, int *s, int *e, int *m) {
    int exp;
    *s = (cf < 0);
    cf = cf << 1;
    if (cf == 0)
        exp = 0;
    else {
        exp = 18;
        while (cf >= 0) {
            cf = cf << 1;
            exp--;
        }
        cf = (cf >> 8) & 0x7FFFFF;
        exp = exp + 127;
    }
    *e = exp;
    *m = cf;
}

El Codi 32.2 descriu l’algorisme bit a bit: alguns dels desplaçaments sobre valors amb signe (cf << 1, cf >> 8) queden fora de la definició estricta del llenguatge C, per això en assemblador es fa servir srli, el desplaçament lògic, que és el que descriu l’algorisme. (Aquí srai donaria el mateix resultat: la màscara 0x7FFFFF esborra els 8 bits que entren per l’esquerra.)

L’algorisme treballa en tres fases. Primer, separa el signe de la magnitud: codifica el signe a *s i elimina el bit de signe de cf desplaçant tots els bits una posició a l’esquerra. En aquest punt la coma implícita ha avançat una posició i se situa entre els bits 13 i 12 (el bit 12 és ara el primer bit fraccionari). Segon, si la magnitud no és zero, normalitza: inicialitza l’exponent a 18 (que correspon a situar la coma a la dreta del bit 31) i desplaça cf a l’esquerra iterativament fins que el bit 31 sigui 1, decrementant l’exponent a cada iteració. Tercer, alinea la mantissa normalitzada als 23 bits de menys pes fent un desplaçament a la dreta de 8 posicions i elimina el bit ocult (posició 23) amb una màscara 0x7FFFFF; finalment, suma 127 a l’exponent per codificar-lo en forma esbiaixada.


Exercici 32.2 (s5_2_1.md i s5_2_1.s) Traduïu a RV32I la subrutina descompon. El fitxer s5_2_1.s ha de contenir únicament la subrutina, sense cap programa principal.

Abans d’escriure cap instrucció, responeu al fitxer s5_2_1.md:

  1. Determineu si descompon és uninivell o multinivell i justifiqueu-ho.
  2. Assigneu cada paràmetre (cf, *s, *e, *m) i la variable local (exp) a un registre i justifiqueu l’elecció.
  3. Dibuixeu el bloc d’activació amb els desplaçaments de cada element.

Comproveu que, amb cfixa = 0x87D18A00, descompon deixa signe = 0x00000001, exponent = 0x0000008D (141 en decimal) i mantissa = 0x007A3140.


Solució 32.2. Pas 1. Uninivell o multinivell.

descompon és uninivell: no fa cap crida. No cal preservar ra.

Pas 2. Assignació de registres.

Els paràmetres arriben per convenció ABI: a0 = cf, a1 = @s, a2 = @e, a3 = @m. Cap valor travessa una frontera de crida, de manera que tots els temporals van a registres t. BA buit.

Variable Registre
cf (es modifica al llarg de la subrutina) t0
exp t1
@s, @e, @m (paràmetres: adreces de destinació dels resultats) a1, a2, a3

Pas 3. Bloc d’activació.

descompon és uninivell i totes les variables s’assignen a registres: BA buit.

s5_2_1.s
.globl descompon

.text

# descompon(int cf, int *s, int *e, int *m)
# a0 = cf, a1 = @s, a2 = @e, a3 = @m
descompon:
        # *s = (cf < 0)
        slt     t2, a0, zero       # t2 = 1 si cf < 0, 0 si no
        sw      t2, 0(a1)          # *s = t2

        # cf = cf << 1  (elimina el bit de signe)
        slli    t0, a0, 1          # t0 = cf << 1

        # if (cf == 0) exp = 0
        li      t1, 0              # exp = 0 (cas zero)
        beqz    t0, desa           # si cf == 0, salta a desar

        # else: exp = 18
        li      t1, 18

        # while (cf >= 0): normalitza
while:
        blt     t0, zero, fiwhile  # si bit 31 és 1 (cf < 0), fi del bucle
        slli    t0, t0, 1          # cf = cf << 1
        addi    t1, t1, -1         # exp--
        j       while
fiwhile:

        # cf = (cf >> 8) & 0x7FFFFF
        # srli (desplaçament lògic): els 8 bits que entren per l'esquerra són
        # 0. Amb srai, que hi propagaria el bit 31 (aquí, 1), el resultat seria
        # el mateix: la màscara 0x7FFFFF esborra aquests bits i el bit ocult.
        srli    t0, t0, 8
        li      t2, 0x7FFFFF
        and     t0, t0, t2         # elimina el bit ocult i bits superiors

        # exp = exp + 127
        addi    t1, t1, 127

desa:
        sw      t1, 0(a2)          # *e = exp
        sw      t0, 0(a3)          # *m = cf

        ret
ExempleComprovació pràctica

Per provar descompon de manera aïllada, creeu temporalment un programa principal mínim al fitxer s5_3_1.s (el de la secció següent) que declari les variables globals i faci la crida:

s5_3_1.s
.data
signe:  .word   0
exponent: .word 0
mantissa: .word 0
cfixa:  .word   0x87D18A00

.text
        la      a0, cfixa
        lw      a0, 0(a0)
        la      a1, signe
        la      a2, exponent
        la      a3, mantissa
        jal     ra, descompon

        li      a7, 93
        li      a0, 0
        ecall

Obriu s5_2_1.s i s5_3_1.s a RARS amb Assemble all files currently open i executeu: en acabar, els valors de signe, exponent i mantissa es llegeixen al panell del segment de dades.

32.3 Subrutina compon i programa complet

La subrutina compon fa el pas invers: rep els tres camps separats i els combina en un únic valor de 32 bits en format IEEE 754 de precisió simple. El signe s’ha de desplaçar al bit 31, l’exponent als bits 30–23, i la fracció (paràmetre mantissa) ja ocupa els bits 22–0:

compon.c
int compon(int signe, int exponent, int mantissa) {
    return (signe << 31) | (exponent << 23) | mantissa;
}

Els tres camps no se superposen (cada un ocupa exclusivament els seus bits), de manera que la combinació amb or és directa sense necessitat de màscares.


Exercici 32.3 (s5_3_1.s) Substituïu el programa principal temporal de la secció anterior pel programa complet. El fitxer s5_3_1.s ha de contenir compon i el programa principal, que crida descompon (definida a s5_2_1.s) i compon en seqüència i escriu el resultat a cflotant.

Abans d’escriure cap instrucció:

  1. Determineu si compon és uninivell o multinivell i justifiqueu-ho.
  2. Determineu si el programa principal és uninivell o multinivell. Identifiqueu quins valors es generen abans d’una crida i s’usen després, i assigneu-los a registres segurs.
  3. Dibuixeu el bloc d’activació del programa principal.

Comproveu que, en acabar, cflotant = 0xC6FA3140, i que, amb els valors inicials de cfixa següents, pren el valor esperat:

cfixa Valor decimal cflotant esperat
0x00000000 \(0{,}0\) 0x00000000
0x80000000 \(-0{,}0\) 0x80000000
0x0000CC00 \(12{,}75\) 0x414C0000

Solució 32.3. Pas 1. compon: uninivell o multinivell.

compon és uninivell: no fa cap crida. Tots els temporals van a registres t; BA buit.

Pas 2. El programa principal: registres segurs.

El programa principal és multinivell: crida descompon i després compon. Els valors generats abans de la primera crida que s’usen després són les adreces de signe, exponent i mantissa, necessàries per llegir els resultats de descompon abans de cridar compon: calen registres segurs per a elles. ra, en canvi, no necessita preservar-se: és el punt d’entrada i mai no torna a cap invocant (acaba amb ecall), de manera que el valor que hi deixa cada jal ra, ... es pot sobreescriure lliurement en la crida següent.

Dada Registre
@signe s0
@exponent s1
@mantissa s2

Pas 3. Bloc d’activació del programa principal.

El programa principal no necessita bloc d’activació, tot i usar registres segurs: com que és el punt d’entrada i no torna mai a cap invocant, no hi ha cap valor previ de s0–s2 (ni de ra) que calgui preservar i restaurar. BA buit.

s5_3_1.s
.data
signe:  .word   0
exponent: .word 0
mantissa: .word 0
cfixa:  .word   0x87D18A00
cflotant: .word 0

.text

        la      s0, signe
        la      s1, exponent
        la      s2, mantissa

        # descompon(cfixa, &signe, &exponent, &mantissa)
        la      a0, cfixa
        lw      a0, 0(a0)
        mv      a1, s0
        mv      a2, s1
        mv      a3, s2
        jal     ra, descompon

        # compon(signe, exponent, mantissa)
        lw      a0, 0(s0)       # a0 = signe
        lw      a1, 0(s1)       # a1 = exponent
        lw      a2, 0(s2)       # a2 = mantissa
        jal     ra, compon      # a0 = cflotant

        la      t0, cflotant
        sw      a0, 0(t0)       # cflotant = a0

        li      a7, 93
        li      a0, 0
        ecall

# compon(int signe, int exponent, int mantissa)
# a0 = signe, a1 = exponent, a2 = mantissa
# retorna el valor IEEE 754 de precisió simple a a0
compon:
        slli    t0, a0, 31      # t0 = signe << 31
        slli    t1, a1, 23      # t1 = exponent << 23
        or      t0, t0, t1      # t0 = (signe << 31) | (exponent << 23)
        or      a0, t0, a2      # a0 = t0 | mantissa
        ret
ExempleComprovació pràctica

Obriu s5_2_1.s i s5_3_1.s a RARS amb Assemble all files currently open i executeu. Per als casos límit, canvieu el valor inicial de cfixa a s5_3_1.s i torneu a assemblar els dos fitxers abans d’executar.

32.4 Errors de precisió

El programa de conversió desenvolupat en aquesta sessió és correcte per a la majoria de valors, però en alguns casos introdueix una pèrdua de precisió inevitable. En aquest exercici se n’analitzen les causes i les condicions en què es produeix.


Exercici 32.4 (s5_4_1.md) Responeu les preguntes següents sobre el programa de conversió de coma fixa a coma flotant:

  1. Quina condició ha de complir el valor inicial de cfixa perquè es produeixi pèrdua de precisió en la conversió? Raoneu la resposta en termes del nombre de bits significatius de cada format.

  2. Indiqueu un valor concret de cfixa per al qual es produeixi pèrdua de precisió. Indiqueu també el valor de cflotant resultant i expliqueu quins bits s’han perdut.

  3. En quina instrucció concreta del codi C de descompon es produeix la pèrdua de precisió?

  4. Quin dels modes d’arrodoniment (Secció 5.1.7) aplica aquest programa de conversió? Justifiqueu-ho.

  5. El format de coma fixa d’aquesta sessió té un rang limitat. Indiqueu un nombre positiu que estigui dins del rang del format IEEE 754 de precisió simple però fora del rang del format de coma fixa. Indiqueu també quina és la potència de 2 més petita que compleix aquesta condició.


Solució 32.4. Pregunta 1.

El format de coma fixa té 31 bits de magnitud; el format IEEE 754 de precisió simple té 24 bits significatius (23 de fracció més el bit ocult). Després de la normalització, l’operació cf >> 8 descarta els 8 bits de menys pes de la mantissa normalitzada (posicions 0–7). Es produeix pèrdua de precisió quan algun d’aquests 8 bits és significatiu (és a dir, val 1), cosa que passa sempre que la magnitud original tingui bits significatius en posicions prou baixes per caure en aquest rang un cop normalitzada.

De manera equivalent i exacta: sigui \(b_{\text{alt}}\) la posició del bit 1 de més pes de la magnitud i \(b_{\text{baix}}\) la posició del bit 1 de menys pes. Es produeix pèrdua de precisió si i només si \(b_{\text{alt}} - b_{\text{baix}} + 1 \geq 25\), és a dir, si la magnitud «ocupa» 25 bits o més entre el seu bit significatiu més alt i el més baix (només 24 d’aquests bits caben al format IEEE 754: el bit ocult més els 23 de fracció).

Pregunta 2.

Un exemple clar és cfixa = 0x7FFFFFFF (magnitud màxima: tots els bits 30–0 a 1, valor \(\approx 2^{19}\)). Després de cf = cf << 1, cf = 0xFFFFFFFE; el bit 31 ja és 1, de manera que el bucle de normalització no itera i exp es manté a 18. El desplaçament cf = (cf >> 8) & 0x7FFFFF dona cf = 0x007FFFFF (la fracció resultant). L’exponent codificat és exp = 18 + 127 = 145 = 0x91. El valor cflotant resultant és 0x48FFFFFF: els 7 bits significatius de menys pes de la magnitud original (tots amb valor 1), més el 0 introduït pel desplaçament inicial cf << 1, s’han descartat, i el nombre representat és lleugerament inferior al valor original.

Pregunta 3.

La pèrdua de precisió es produeix a la instrucció:

descompon.c
cf = (cf >> 8) & 0x7FFFFF;

El desplaçament a la dreta de 8 posicions descarta els 8 bits de menys pes de la mantissa normalitzada. Si algun d’aquests bits era significatiu, la informació es perd de manera irreversible.

Pregunta 4.

El programa aplica el mode d’arrodoniment a zero (round toward zero): el desplaçament >> 8 descarta els bits de menys pes sense cap ajust (truncament).

Pregunta 5.

El rang del format de coma fixa d’aquesta sessió és aproximadament \([-(2^{19}-2^{-12}),\ +(2^{19}-2^{-12})]\), és a dir, el valor positiu màxim és lleugerament inferior a \(2^{19} = 524\,288\). Qualsevol nombre positiu \(\geq 2^{19}\) és dins del rang IEEE 754 de precisió simple però fora del rang de coma fixa. El menor nombre potència de 2 que compleix aquesta condició és:

\[2^{19} = 524\,288\]