Interne voorstelling Het geheugen wordt ingedeeld in een aantal gebieden van gelijke grootte. Een gebied van 8 bits noemt men een byte (nible een groep van 4 bits). Een (computer)woord bestaat, afhankelijk van de computer architectuur, uit een 2 bytes (16 bits), 4 bytes (32 bits) of 8 bytes (64 bits). Het geheugen kan men voorstellen als een opeenvolging van vakken die elk uit 8 bits of één byte bestaan. Iedere geheugenplaats heeft een welbepaalde unieke identificatie (volgnummer) die men adres noemt. In het geheugen moeten programma en data gestockeerd worden. types en conversies De grootte van een geheugen wordt meestal aangeduid in Kb, Mb of Gb. 1 Kb = 1 Mb = 1 Gb 1 kilobyte 210 bytes = 1 megabyte = = 2 1 gigabyte = 20 = 1024 bytes bytes = 1048576 bytes 230 bytes = 1073741824 bytes Binaire en andere talstelsels ons vertrouwde decimale talstelsel: grondtal is de waarde 10 Voorstelling van gegevens: bits en bytes waarde van een cijfer: bepaald door positie van dat cijfer in getal Een (digitale) computer werkt steeds met binaire grootheden. De reden daartoe is het feit dat de computer bestaat uit een aantal onderdelen die slechts twee toestanden kunnen aannemen bistabiele elementen; vb: flip-flop-schakelingen. De ene toestand wordt conventioneel voorgesteld als 1 en de andere als 0. Gewoonlijk wordt dit aangeduid door de technische term BIT (BInary digiT = binair cijfer). Een bit is dus de kleinst mogelijke informatie-eenheid in een computersysteem. Bits : bouwstenen voor grotere betekenisvolle informatiehoeveelheden. Elke vorm van informatie wordt gemanipuleerd in de vorm van een combinatie van enen en nullen. 1063 = 1 × 103 + 0 × 102 + 6 × 101 + 3 × 100 In dit talstelsel zijn tien verschillende cijfers nodig. In het binaire talstelsel is het grondtal 2 : 7 6 twee cijfers: de 0 en de 1 (bit). 01101011 = 0 × 2 + 1 × 2 + 1 × 2 + 0 × 24 + 1 × 23 + 0 × 22 + 1 × 21 + 1 × 20 + 64 5 + 32 27 0 8 + 2 + 1 20 1 MSB Most Significant Bit 1 0 1 0 1 1 LSB Least Significant Bit getal 107 teken k Een getal in binaire voorstelling is meestal erg lang. het octale talstelsel met grondtal 8 Handiger voorstelling: het hexadecimale talstelsel met grondtal 16 • een functie die de binaire voorstelling van een positief geheel getal omzet in decimale vorm: de oproep b2d(7,bits) met in bits 1 0 0 1 0 1 1 omzetting van een binair getal naar een octaal getal: vanaf de LSB de bits samen te nemen in groepjes van drie; resultaat 75. Bij een hexadecimaal getal zijn dit groepjes van vier bits. 0 1 1 0 1 0 1 1 1 5 begin bij de MSB • Algoritme: 3 6 vermenigvuldig met 2 en tel de volgende bit er bij op b • Voorbeeld: In het octale talstelsel gebruikt men de cijfers van 0 tot 7 1 0 het hexadecimale talstelsel: naast de 10 decimale cijfers nog 6 extra symbolen namelijk de letters van ’a’ (’A’) tot en met ’f’ (’F’). 0153 = 1 × 82 + 5 × 81 + 3 × 80 0x6b = 6 × 161 + 11 × 160 • Voorbeeld: 1 1 18 0 Binaire voorstelling : 100 1011 9 1 4 0 0 4 2 1 8 4 0 1 18 9 36 18 37 1 74 75 # d2b .py : decimaal naar binair • een functie die een positief geheel getal omzet in binaire voorstelling: de oproep d2b(75,bits) resultaat 7 met in bits: 1 0 0 1 0 1 1 indien getal even dan LSB=0 anders LSB=1 • Algoritme: deel door 2, indien quotiënt even dan bit = 0, anders 1 herhaal vorige stap tot quotiënt 1 is 37 2 1 Omzettingsfuncties 75 herhaal vorige stap tot de LSB er bij opgeteld is 2 0 1 1 def d2b ( n ) : l = [] while n > 0 : i f n % 2 == 0 : l . append ( 0 ) else : l . append ( 1 ) n /= 2 # b2d.py : binair naar decimaal def b2d ( l ) : r = l [0] fo r i in ra ng e ( 1 , l e n ( l ) ) : r = r ∗ 2 + l[i] return r l . reverse () return l ✞ >>> a = 7 5 ; b = 0113 >>> print bin ( a ) , o c t ( a ) , hex ( a ) , i n t ( b ) 0 b1001011 0113 0x4b 75 ✝ ☎ ✆ Een bit: maar twee mogelijke waarden hebben: 0 of 1. het minteken van een geheel getal Voorstelling de komma van een reëel getal 10 16 20 1 0000 f 15 17 0 1111 d e 14 13 15 16 0 1110 0 1101 b c 12 11 13 14 0 1100 0 1011 9 a 10 9 11 12 0 1010 0 1001 7 8 8 7 7 10 0 1000 0 0111 5 6 6 5 5 6 0 0110 0 0101 3 4 4 3 3 4 0 0100 0 0011 2 2 2 0 0010 0 1 1 0 1 0 0 0001 0 0000 hexadecimaal decimaal octaal binair enkele getallen in de verschillende talstelsels: Gehele getallen Negatieve gehele getallen worden voorgesteld in de twee-complement-vorm. Hoogste bit: tekenbit : stelt de kleinst mogelijke negatieve waarde voor. Wanneer 16 bits gebruikt worden is dit gelijk aan −215 = −32768. De waarde van de binaire voorstelling van een andere negatief getal wordt berekend door de absolute waarde van het 15 bits-getal bij -32768 op te tellen. getal voorstelling verklaring -1 1111 1111 1111 1111 -32768 + 32767 = -1 -2 1111 1111 1111 1110 -32768 + 32766 = -2 -3 1111 1111 1111 1101 -32768 + 32765 = -3 -4 1111 1111 1111 1100 -32768 + 32764 = -4 ... ... ... -32767 1000 0000 0000 0001 -32768 + 1 = -32767 -32768 1000 0000 0000 0000 -32768 + 0 = -32768 Bereik van gehele getallen bij 16 bits Een geheel getal kan in twee bytes (16 bits) gestockeerd worden. Eén bit is voorbehouden als tekenbit. Het grootste positieve getal is dus 215 − 1 = 32767. getal voorstelling 0 0000 0000 0000 0000 1 0000 0000 0000 0001 2 0000 0000 0000 0010 3 0000 0000 0000 0011 4 0000 0000 0000 0100 ... ... 32766 0111 1111 1111 1110 32767 0111 1111 1111 1111 1000 0000 0000 0010 1000 0000 0000 0001 -32766 1111 1111 1111 1101 -3 1111 1111 1111 1110 -32767 -2 1000 0000 0000 0000 -32768 -1 1111 1111 1111 1111 0111 1111 1111 1111 32767 0 0000 0000 0000 0000 32766 1 0111 1111 1111 1110 0111 1111 1111 1101 32765 2 3 0000 0000 0000 0001 0000 0000 0000 0010 0000 0000 0000 0011 Berekenen van de 2-complement voorstelling Decimale voorstelling van twee-complement hexadecimaal getal FFB4 bepaal de binaire voorstelling van de absolute waarde van het getal; complementeer elke bit en tel bij dit complement 1 op; het resultaat is de 2-complement voorstelling. -2 binaire voorstelling van 2 complement 1 bijtellen resultaat decimale voorstelling van complement 1 bijtellen resultaat 0000 0000 0000 0010 1111 1111 1111 1101 0000 0000 0000 0001 1111 1111 1111 1110 2 Om de decimale waarde van een 2-complement voorstelling te berekenen: decimale voorstelling van complement 1 bijtellen resultaat 1111 1111 1111 0001 0000 0000 0000 1110 0000 0000 0000 0001 0000 0000 0000 1111 0 1 1 0 4 2 1 8 4 1111 1111 1011 0100 0000 0000 0100 1011 0000 0000 0000 0001 0000 0000 0100 1100 1 0 18 9 0 38 19 76 38 76 Oplossing : -76 dus -15 Reële getallen benaderende voorstelling in wetenschappelijke notatie: Twee-complement (16 bits) hexidecimale voorstelling van -164 164 82 41 20 10 5 0.5 × 106 2 − 0.999 × 1016 0.642 × 10−23 1 gedeelte voor het × teken : mantisse (of significand) daarna komt de basis (normaal 10) gevolgd door de exponent 0 0 1 0 0 binaire voorstelling van 164 complement 1 bijtellen resultaat of hexadecimaal : FF5C 1 0 0000 0000 1010 0100 1111 1111 0101 1011 0000 0000 0000 0001 1111 1111 0101 1100 1 In deze floating point vorm: elk reëel getal geschreven met behulp van twee gehele getallen 123.456 = 0.123456 × 103 123456 = 6 0.000123456 = 0.123456 × 10 0.123456 × 10−3 123456 en 3 123456 en 6 123456 en − 3 op deze manier: een reëel getal op verschillende manieren voor te stellen ANSI/IEEE 754-1984 norm Voorbeeld Om tot een uniforme manier te komen, bestaan er standaards. Er wordt met een basis 2 gewerkt en door de mantisse te beperken tot het interval [1.0, 2.0[ is ook de exponent uniek gedefinieerd. S exponent (E) mantisse (M ) 1 tekenbit, type float : 4 bytes : 8 bits voor de exponent en 23 bits voor de fractie van de mantisse interne voorstelling van reële getal 10.0 : of S (−1) × M × 2 E bijv. 0 10.0 = (−1) × 1.25 × 2 de mantisse M is steeds van de vorm 1.f → alleen het fractiegedeelte f hoeft gestockeerd te worden Bijvoorbeeld 10.0 = (−1)0 × 1.25 × 23 3 0.25 = 0 × 2−1 + 1 × 2−2 + 0 × 2−3 + 0 × 2−4 + . . . tekenbit: positief: 0 mantisse: 10 exponent: 3 met excess 127 → 130 of 1000 0010 binair −→ 0 Slechts een beperkt aantal bytes voor mantisse en exponent : - in deze binaire string de eerste bit: het teken van de mantisse, - volgende bits: zowel tekenbit als waardebits van de exponent - de resterende bits: waardebits van fractiegedeelte (f ) van de mantisse 1010 100 0001 0 normaliseer −→ 1.010 × 23 010 0000 0000 0000 0000 0000 Een kortere schrijfwijze mbv. hexadecimale voorstelling: 0x41200000. Beide delen moeten ook negatieve getallen kunnen voorstellen: bij de mantisse wordt met een tekenbit (S) gewerkt de exponent : excess 127 mode bij de effectieve waarde wordt 127 bijgeteld resultaat is positief maar 127 te groot bit-patroon exponent verklaring 0000 0000 -127 0-127 Tweede voorbeeld interne voorstelling van reële getal -75.0 : −75.0 = (−1)1 × 1.171875 × 26 ... ... ... tekenbit: negatief: 1 0111 1110 -1 126-127 mantisse: 75 0111 1111 0 127-127 exponent: 6 met excess 127 → 133 of 1000 0101 1000 0000 1 128-127 1000 0001 2 129-127 1000 0010 3 130-127 ... ... ... 1111 1111 128 255-127 binair −→ 1 1001011 100 0010 1 normaliseer −→ 1.001011 × 26 001 0110 0000 0000 0000 0000 Een kortere schrijfwijze mbv. hexadecimale voorstelling: 0xc2960000. Nauwkeurigheid : gehele getallen Elementaire types Met gehele getallen (int en aanverwanten) kan exact gerekend worden. Slechts een beperkt aantal bytes voorzien om een geheel getal te stockeren : Python kent de volgende elementaire types: int float complex bool ✞ >>> a = 4 ; t ype ( a ) <t ype ’ i n t ’> >>> b = 3 . 0 ; t ype ( b ) <t ype ’ f l o a t ’> >>> c = ’ a p p e l ’ ; t ype ( c ) <t ype ’ s t r ’> >>> d = True ; t ype ( d ) <t ype ’ b o o l ’> >>> z = 3 + 4 j ; t ype ( z ) <t ype ’ complex ’> ✝ str t e k l e i n = −9223372036854775810 Overflow getal te groot en positief om het te stockeren in de variabele. Underflow getal te groot en negatief om het te stockeren in de variabele. Bij het rekenen met gehele getallen kan dus overflow ontstaan. De meeste programmeertalen signaleren dit niet maar de resultaten zijn fout! Op processor-niveau kan het via de overflow-vlag gecontroleerd worden. a = 20000 ✆ Complexe waarden ✞ >>> a = 2 + 3 j >>> a . r e a l , a . imag (2.0 , 3.0) >>> b = complex ( −1 ,4) >>> a+b (1+7 j ) >>> a − b (3−1 j ) >>> a ∗ b (−14+5 j ) >>> a / b ( 0 .5 8 8 2 3 5 2 9 4 1 1 7 6 4 7 0 8 − 0 .6 4 7 0 5 8 8 2 3 5 2 9 4 1 1 8 j ) >>> a ∗∗ 0 . 5 ( 1 .6 7 4 1 4 9 2 2 8 0 3 5 5 4 0 1 + 0 .8 9 5 9 7 7 4 7 6 1 2 9 8 3 8 0 2 j ) ✝ t e g r o o t = 9 2 2 3 3 7 2 0 3685477 5810 ☎ ☎ ✆ 40000 20000 10000 5000 2500 1250 625 312 156 78 39 19 9 4 2 1 0100 1110 0010 0000 b = 20000 0100 1110 0010 0000 de som a + b : 1001 1100 0100 0000 → → → → → → → → → → → → → → → → 0 0 0 0 0 0 1 0 0 0 1 1 1 0 0 1 binaire voorstelling : ( −25536) 1001 1100 0100 0000 MSB is een 1 → negatief getal in 2-complement inverteren 0110 0011 1011 1111 plus 1 1 0110 0011 1100 0000 1 3 6 12 24 49 99 199 399 798 1596 3192 6384 12768 25536 bijhorende gehele decimale waarde : -25536 Voor de types van numerieke waarden (int en float) wordt in geheugen een plaats met een vaste lengte voorzien, namelijk 8 bytes. Bij deze binaire voorstellingen wordt telkens één bit gebruikt als tekenbit. Berekening van een determinant Op het eerste zicht lijkt de floating point voorstelling zeer nauwkeurig. Het grootste positieve geheel getal dat kan voorgesteld worden is in binaire voorstelling 63 opeenvolgende eentjes of 263 − 1. Maar sommige numerieke berekeningen zijn inherent numeriek instabiel zodat zeer kleine fouten zeer snel kunnen groeien. Wanneer bij dit getal 1 bijgeteld wordt, zou de hoogste bit op 1 komen, en zou er dus een negatieve waarde ontstaan. Python schakelt echter over naar waarden van type long: ✞ >>> 9 2 2 3 3 7 2 0 3685477 5807 + 1 9 2 2 3 3 7 2 0 368547 75808L ✝ ☎ ✆ Schrijf een programma dat een vierkante matrix a genereert met aij = 1/(i + j − 1) voor 1 ≤ i ≤ n en 1 ≤ j ≤ n (dit is een Hilbert matrix van orde n). Laat het programma voor verschillende n waarden de determinant berekenen en afdrukken. 1 1 2 1 3 1 4 1 2 1 3 1 4 1 3 1 4 1 5 1 4 1 5 1 6 1 5 1 6 1 7 Waarden van dit type worden aangegeven door op het einde van het getal de letter L te plaatsen. # determinant .py Nauwkeurigheid : reële getallen # Aangezien de mantisse uit een beperkt aantal bits bestaat, zijn er ook slechts een beperkt aantal beduidende cijfers te stockeren. Dit impliceert eventueel verlies van nauwkeurigheid. Hoeveel bits voorzien worden voor exponent en mantisse is systeemafhanelijk. nauwkeurigheid float32 (4 bytes) float (8 bytes) 1 + 8 + 23 bits 1 + 11 + 52 bits 2−23 1.19 × 10−7 2−52 6 cijfers ondergrens 2−126 bovengrens 2128 bereik 1.18 × 10−38 1.18 × 1038 [−38, 38] 2.22 × 10−16 15 cijfers 2−1022 21024 bereken determinant met a[i][j] = 1/( i+j+1) 2.23 × 10−308 (i en j starten bij rij 0 en kolom 0) import numpy def h o e g r o o t ( ) : i n v o e r = r a w i n p u t ( ’ Geef g r o o t t e : ’ ) n = int ( invoer ) return n def a fdrukke n ( a , n ) : fo r i in ra ng e ( n ) : fo r j in ra ng e ( n ) : 2.23 × 10308 [−308, 308] print ’ 1 3 . 7 f ’ % ( a [ i ] [ j ] ) , print print else : def g e nm a t rix ( x , n ) : fo r i in ra ng e ( n ) : de t = 1 . 0 fo r i in ra ng e ( 1 , n ) : fo r j in ra ng e ( n ) : x [ i ] [ j ] = 1 . 0 / ( i+j +1.0) def de t e rm ina nt ( x , n ) : de t ∗= x [ i ] [ i ] return de t def main ( ) : EPS = 1 . 0 E−8 m = hoegroot ( ) wissel = 0 fo r n in ra ng e ( 2 ,m) : fo r i in ra ng e ( n−1) : a = numpy . z e r o s ( [ n , n ] , numpy . f l o a t 3 2 ) rmax = i g e nm a t rix ( a , n ) fo r r in ra ng e ( i +1 ,n ) : # i f abs ( x [ r ] [ i ] ) > abs ( x [ rmax ] [ i ] ) : rmax = i # w i s s e l += 1 fo r k in ra ng e ( i , n ) : temp = x [ i ] [ k ] x [ i ] [ k ] = x [ rmax ] [ k ] x [ rmax ] [ k ] = temp fo r r in ra ng e ( i +1 ,n ) : faktor = x [ r ] [ i ]/x [ i ] [ i ] else : print ’PANIEK ’ , ’ n= ’ , n , ’ i= ’ , i , ’ r= ’ , r exit () fo r k in ra ng e ( i , n ) : x [ r ] [ k ] = x [ r ] [ k ] − f a k t o r ∗x [ i ] [ k ] i f w i s s e l% 2 == 1 : de t = −1.0 afdrukken(a,n) print ’+++++ n= ’ , n , ’ de t e rm ina nt i s ’ , d i f rmax > i : i f abs ( x [ i ] [ i ] ) > EPS : afdrukken(a,n) d = de t e rm ina nt ( a , n ) # startoproep main ( ) Het resultaat voor verschillende n waarden wanneer met float32 (enkelvoudige precisie) en met float64 (dubbele precisie) gerekend wordt: Type casting: expliciet een conversie opleggen oproepen van gewenste type functie met argument de te converteren waarde Omvorming van int waarden naar float waarden: n float32 float64 2 8.333334e-02 8.333333e-02 3 4.629642e-04 4.629630e-04 2160−1 = 4.629630e-04 4 1.653463e-07 1.653439e-07 6048000−1 = 1.653439e-07 5 3.751022e-12 3.749295e-12 6 5.456915e-18 5.367300e-18 i = 3 exact 12 −1 266716800000 −1 = 8.333333e-02 = 3.749295e-12 # tcast .py: omzetten van types import numpy def main ( ) : a = numpy . z e r o s ( [ 5 ] , f l o a t ) 7 7.336620e-25 4.835803e-25 j = 2 8 2.532158e-32 2.737050e-33 a[0] = i/j Merk op dat in het programma slechts op één plaats een aanpassing moet gebeuren voor de berekening in enkelvoudige of dubbele precisie: a = numpy . z e r o s ( [ n , n ] , numpy . f l o a t 3 2 ) a = numpy . z e r o s ( [ n , n ] , numpy . f l o a t 6 4 ) Resultaat van dit programma: a [ 1 ] = f l o a t ( i )/ j a [2] = i/ float ( j ) [ 1. print a # startoproep main ( ) Type conversie Als operanden in een expressie van verschillend type zijn, dan worden deze operands naar een gemeenschappelijk type geconverteerd. operand 1 str int int float operand 2 int float complex complex gemeenschappelijk type niet mogelijk float complex complex Er wordt dus telkens impliciet geconverteerd naar het “grotere” type. Daardoor zal zo weinig mogelijk informatie omwille van afkapping verloren gaan. een van “groter” type naar “kleiner” type ✞ toekenningsexpressie: een conversie ☎ >>> i = 31 bij deling: gehele waarde i naar float >>> f = 4 . 0 deling van twee floats, dus geeft 7.75 >>> r e s = i n t ( i / f ) via int () functie: toekenning van 7 ✝ ✆ Begrippen • Talstelsels. • Interne voorstelling van gehele en reële getallen. • Elementaire types. • Conversie operaties: impliciet en expliciet. 1.5 1.5 0. 0. ]