Python:从两个32位整型创建定点小数(一个用于整数部分,一个用于小数部分)
我有一个从文件中提取的64位时间戳,这个时间戳的前32位表示秒数,后32位表示秒的小数部分。我现在不知道怎么把后32位转换成小数,而不想一个比特位一个比特位地去处理。
有没有什么建议?
举个例子,数字 4ca1f350 9481ef80
转换成 1285682000.580107659
。
补充说明:这些数据来自一个数据包捕获设备,我看到的文档说小数部分大约有纳秒级的精度(具体来说,它输出32位中的29位,大约是2纳秒)。
3 个回答
你没有说明“秒”是从什么时候开始的。看起来是从1970年1月1日开始的。你可以计算一个调整值,这个值是从1970年1月1日到你预期的最小值之间的秒数。然后你可以调整每个值... vadj = float(hi32 - fudge) + lo32 / 2.0 ** 32
如果最大值(hi32)和最小值(lo32)之间的差距小于大约6天(这应该足够用于数据包捕获的练习?),那么你只需要19位来表示hi32 - fudge。19位加上32位就是51位——这在Python的浮点数精度范围内,记得没错的话。
现在很晚了,所以我不打算做详细分析,但以上内容应该能让你明白大概意思。
编辑:为什么@unwind的回答不适用:
>>> a = 0x00000001/4294967296.0 + 0x4ca1f350
>>> b = 0x00000002/4294967296.0 + 0x4ca1f350
>>> b - a
0.0
>>>
编辑2:除了str()、repr()、timestamp_from_str(),你还想对时间戳做什么操作?我能想到的差异就是这些。你可以用类似这样的方式:
>>> 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'
>>>
关于我“如果必须”的评论:如果观察的时间间隔超过6天,你真的需要精确到最后一秒(秒 / 2 ** 32)吗?在我看来,如果你用float(difference(ts1, ts2))
而不是float(ts1) - float(ts2)
,应该就没问题。
编辑3:模糊/不一致警告
请编辑你的问题,解决以下问题:
你在评论中说“我正在查看的文档说,分数部分具有纳秒级精度(具体来说,它输出32位中的29位)”。请提供该文档的链接。
一秒钟有1000000000(10**9
)纳秒。人们会期望分数部分需要math.log(10**9, 2)
向上取整(即29.897352853986263向上取整为30)位,而不是29位。请解释一下。
请回答:在32位中,哪29位或30位包含分数部分,哪3位或2位始终为零?
其次,人们会期望通过除以10**9
将纳秒转换为秒。然而你在问题中的陈述“数字4ca1f350 9481ef80转换为1285682000.580107659”与除以2**32
是一致的。实际上0x9481ef80是2,491,543,424,这大于两倍的10**9
。请解释一下。“转换为”的说法来源于哪里?你还有其他例子吗?
你可以直接把这个十六进制的数字除以它能达到的最大值,这样就能得到正确的比例:
>>> float(0x9481ef80) / 0x100000000
0.58010765910148621
为了准确表示一个数的整数部分和小数部分的总和(32位加29位等于61位),你需要使用Decimal(默认有28位小数,这样可以表示93位的数字),
>>> from decimal import Decimal
>>> Decimal(0x9481ef80) / Decimal(2**32) + Decimal(0x4ca1f350)
Decimal('1285682000.580107659101486206')
或者使用Fraction(精确表示),
>>> from fractions import Fraction
>>> Fraction(0x9481ef80, 2**32) + Fraction(0x4ca1f350)
Fraction(43140329262089183, 33554432)
>>> float(_)
1285682000.5801077
需要注意的是,float使用的是“IEEE双精度格式”,所以它只能保持53位的精度:
>>> a = 0x9481ef80 / 2**32 + 0x4ca1f350
>>> b = 0x9481ef90 / 2**32 + 0x4ca1f350
>>> a == b
如果你把小数部分单独存储为一个变量,那这样做没问题,但如果是这样,为什么不直接保持原样呢?
>>> 0x9481ef80 / 2**32
0.5801076591014862
>>> 0x9481ef90 / 2**32
0.5801076628267765