Interne voorstelling types en conversies

advertisement
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. ]
Download