1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
| # -*- coding: utf-8 -*-
from struct import pack, unpack
# attention : on va traiter une chaine de caracteres (de bits)
# en écriture "conventionnelle", avec les bits de poids forts à gauche
# Les offsets sont données avec cette convention
# avec les clés 'S'(igne), 'E'(xposant) et 'M'(mantisse)
# ainsi que les chaines dans TITRE, DIZAINES, UNITES et ENTETE
# pour bien montrer que l'on va du poids fort au poids faible
TITRE = { 4 : 'B31' + '.'*(32-5) + 'B0',
8 : 'B63' + '.'*(64-5) + 'B0'}
# affichage des dizaines du nos de bits
DIZAINES = { 4 : ''.join([str(i)+' '*9 for i in range(4)])[:32][::-1],
8 : ''.join([str(i)+' '*9 for i in range(7)])[:64][::-1]}
# affichage des dizaines du nos de bits
UNITES = { 4 : ''.join(map(lambda n:str(n%10),range(32)))[::-1],
8 : ''.join(map(lambda n:str(n%10),range(64)))[::-1]}
# on mixe les 3 lignes d'affichage
ENTETE = { 4 : '\n'.join((TITRE[4],DIZAINES[4],UNITES[4])),
8 : '\n'.join((TITRE[8],DIZAINES[8],UNITES[8]))}
# où démarre chaque partie ?
OFFSET = { 4: { 'S': 0, 'E': 1, 'M': 9},
8: { 'S': 0, 'E': 1, 'M': 12}}
# quelles tailles ont-elles ?
SIZE = { 4: { 'S': 1, 'E': 8, 'M': 23},
8: { 'S': 1, 'E': 11, 'M': 52}}
# décalage de l'exposant sur 4 et 8 octets
DECALAGE = { 4 : -127, 8 : -1023}
def dump_nombre(nombre,size):
# liste des octets en mode little-endian - octets de poids fort à gauche
octets = unpack('%dB' % size, pack('f' if size == 4 else 'd',nombre))
# chaine avec les bits de b31 ou b63 (à gauche) à b0 (à droite)
bits = ''.join([str((byte>>d)&1) for byte in reversed(octets) for d in range(7,-1,-1)])
# visualisation des bits correspondants
print ENTETE[size]
print bits
# et mise en évidence des différentes parties
for p, partie in (('S','signe'),('E','exposant'),('M','mantisse')):
start, end = OFFSET[size][p], OFFSET[size][p]+SIZE[size][p]
chars = ['.']*(size*8)
chars[start:end] = bits[start:end]
print ''.join(chars), partie
# reconstruction du nombre : le signe
start = OFFSET[size]['S']
end = start + SIZE[size]['S']
str_signe = bits[start:end]
signe = pow(-1,int(str_signe))
print 'signe :', signe
# reconstruction du nombre : l'exposant
start = OFFSET[size]['E']
end = start + SIZE[size]['E']
str_exposant = bits[start:end]
exposant = 0
for b, bit in enumerate(reversed(str_exposant)):
exposant += int(bit) * pow(2,b)
print 'exposant : %d%+d = %d' % (exposant,DECALAGE[size],(exposant+DECALAGE[size]))
exposant += DECALAGE[size]
# reconstruction du nombre : la mantisse
start = OFFSET[size]['M']
end = start + SIZE[size]['M']
str_mantisse = bits[start:end]
mantisse = 0.
print "contributions des bits de la mantisse"
for b, bit in enumerate(str_mantisse,start=1):
if bit == '0': continue
current = 1./pow(2,b)
print "\tB%-2d" % (SIZE[size]['M']-b), current
mantisse += current
print "mantisse : (1 +) ", mantisse
# le bit caché
mantisse += 1.
reconstruit = signe * pow(2,exposant) * mantisse
print "au départ : %.16f" % nombre
significatifs = 7 if size == 4 else 16
fmt = "nombre reconstruit à la mano : %%.%df" % significatifs
print fmt % reconstruit
# test de l'égalité '==' entre les deux nombres
# ATTENTION : si on a travaillé avec 4 octets, l'égalité n'est pas assurée
# (certains rééels double précision NE peuvent PAS être EXACTEMENT retranscris en
# réels simple précision)
# pour vérifier que la reconstruction s'est quand même bien passée, on compare
# le résultat via pack en réel simple précision
print "egalite '==' entre les 2 valeurs :", nombre == reconstruit
if size == 4:
print "égalité '==' entre pack('f') des 2 valeurs :", pack('f',nombre) == pack('f',reconstruit)
if __name__ == '__main__':
import sys
import math
if sys.argv[1].lower() == 'pi':
nombre = math.pi
elif sys.argv[1].lower() == 'e':
nombre = math.e
else:
nombre = sys.argv[1]
dump_nombre(float(nombre), int(sys.argv[2]) if 2 < len(sys.argv) else 8) |
Partager