Python: créer décimal à virgule fixe à partir de deux ints 32 bits (pour une partie d'int, pour une décimale)

StackOverflow https://stackoverflow.com/questions/3813990

  •  26-09-2019
  •  | 
  •  

Question

I ai un horodatage 64 bits décompressé à partir d'un fichier contenant des données binaires, où les 32 bits supérieurs sont au nombre de secondes et les 32 bits inférieurs sont la fraction de la seconde. Je suis coincé avec la façon de convertir réellement les 32 bits inférieurs en une fraction sans boucle à travers ce bit par bit.

Toutes les suggestions?

Pour référence, le nombre 4ca1f350 9481ef80 se traduit 1285682000.580107659

Edit: Pour le contexte: les données proviennent d'un dispositif de capture de paquets et de la documentation que je l'ai vu, dit que la partie fractionnaire a à peu près la précision nano-seconde (plus précisément il émet 29 des 32 bits, donnant ~ 2 ns)

.
Était-ce utile?

La solution

Pour représenter la somme de partie intégrante et fractionnée avec suffisamment de précision (32 + 29 = 61 Bits), il faut un nombre décimal (28 chiffres décimaux par défaut, ce qui est suffisant pour 93 Bits),

>>> from decimal import Decimal
>>> Decimal(0x9481ef80) / Decimal(2**32) + Decimal(0x4ca1f350)
Decimal('1285682000.580107659101486206')

ou Fraction (exact),

>>> from fractions import Fraction
>>> Fraction(0x9481ef80, 2**32) + Fraction(0x4ca1f350)
Fraction(43140329262089183, 33554432)
>>> float(_)
1285682000.5801077

Notez qu'un flotteur utilise "format IEEE double" il ne peut contenir que 53 bits de précision:

>>> a = 0x9481ef80 / 2**32 + 0x4ca1f350
>>> b = 0x9481ef90 / 2**32 + 0x4ca1f350
>>> a == b

Il est très bien si vous stockez la partie décimale comme sa propre variable, mais si tel est le cas, pourquoi ne pas simplement le garder tel quel?

>>> 0x9481ef80 / 2**32
0.5801076591014862
>>> 0x9481ef90 / 2**32
0.5801076628267765

Autres conseils

Vous pouvez simplement diviser le nombre hexadécimal du possible d'obtenir le rapport correct maximal:

>>> float(0x9481ef80) / 0x100000000
0.58010765910148621

Vous ne dites pas que secondes depuis quand . On dirait qu'il est depuis 1970-01-01. Vous pouvez calculer un facteur de fudge qui est le nombre de secondes entre l'époque (1970-01-01) et votre valeur la plus basse attendue. Ensuite, vous ajustez chaque valeur ... vadj = float(hi32 - fudge) + lo32 / 2.0 ** 32

Si la différence entre max (hi32) et min (lo32) est inférieure à environ 6 jours de dollars (devrait être suffisant pour un exercice de capture de paquets ()?), Alors vous avez besoin seulement 19 bits pour hi32 - Fudge. 19 bits + 32 bits est de 51 bits de - dans la précision d'un IIRC flotteur python.

Il est tard ici, donc je ne vais pas faire une analyse détaillée mais devrait vous donner ci-dessus l'image.

Edit: pourquoi la réponse de @ dérouleur ne fonctionne pas:

>>> a = 0x00000001/4294967296.0 + 0x4ca1f350
>>> b = 0x00000002/4294967296.0 + 0x4ca1f350
>>> b - a
0.0
>>>

Edit 2: Quelles sont les opérations que vous voulez faire un horodatage en dehors de str (), repr (), timestamp_from_str ()? La différence est de tout ce qui vient à l'esprit. Vous pouvez utiliser quelque chose comme ceci:

>>> class TS64(object):
...   def __init__(self, hi, lo):
...     self.hi = hi
...     self.lo = lo
...   def float_delta(self, other):
...     hi_delta = self.hi - other.hi
...     # check that abs(hi_delta) is not too large, if you must
...     return hi_delta + (self.lo - other.lo) / 4294967296.0
...
>>> a = TS64(0x4ca1f350, 1)
>>> b = TS64(0x4ca1f350, 2)
>>> b.float_delta(a)
2.3283064365386963e-10
>>> repr(_)
'2.3283064365386963e-10'
>>>

A propos de mon « si vous devez » commentaire: Si les observations sont plus de 6 jours d'intervalle, avez-vous vraiment besoin de précision jusqu'à la dernière (deuxième / 2 ** 32) ??? À mon humble avis, si vous le faites float(difference(ts1, ts2)) au lieu de float(ts1) - float(ts2), vous devriez être OK.

Edit 3: Ambiguïté / alerte inconsistance

S'il vous plaît modifier votre question pour aborder les questions suivantes:

Vous dites dans un commentaire que « » « la documentation que je regarde, dit que la partie fractionnaire a une précision nano-seconde (plus précisément il émet 29 des 32 bits) » « ». S'il vous plaît fournir une URL pour cette documentation.

Il y a 1000000000 (10**9) nanosecondes dans une seconde. On pourrait attendre la partie décimale d'exiger math.log(10**9, 2) arrondie (à savoir 29,897352853986263 arrondie à savoir 30) bits, et non 29. S'il vous plaît expliquer.

S'il vous plaît répondre: Sur les 32 bits disponibles, dont 29 ou 30 bits contiennent la partie décimale et dont 3 ou 2 bits sont toujours zéro

En second lieu on peut s'y attendre à convertir les nanosecondes en secondes en divisant par 10**9. Toutefois, votre déclaration dans votre question « » « le nombre 4ca1f350 9481ef80 se traduit par 1.285.682.000,580107659 » « » est compatible avec la division par 2**32. En fait 0x9481ef80 est 2491543424 qui est supérieure à 10**9 deux fois. S'il vous plaît, expliquez. Quelle est la source de l'instruction « se traduit »? Avez-vous d'autres exemples?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top