Python:从两个 32 位整数创建定点小数(一个用于整数部分,一个用于十进制)

发布于 2024-09-25 09:45:23 字数 276 浏览 4 评论 0原文

我有一个从二进制数据文件中解压出来的 64 位时间戳,其中前 32 位是秒数,后 32 位是秒的小数部分。我一直困惑于如何将底部 32 位实际转换为分数,而不需要逐位循环它。

有什么建议吗?

作为参考,数字 4ca1f350 9481ef80 转换为 1285682000.580107659

编辑: 对于上下文:数据来自数据包捕获设备,我看到的文档说小数部分的精度大约为纳秒(具体来说,它输出 32 位中的 29 位,给出约 2ns)。

I have a 64-bit timestamp unpacked from a file with binary data, where the top 32 bits are the number of seconds and the bottom 32 bits are the fraction of the second. I'm stuck with how to actually convert the bottom 32 bits into a fraction without looping through it bit-by-bit.

Any suggestions?

For reference, the number 4ca1f350 9481ef80 translates to 1285682000.580107659

Edit:
For context: the data comes from a packet capture device and the documentation I've seen says that it the fractional part has roughly nano-second precision (specifically it outputs 29 of the 32 bits, giving ~2ns).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

煞人兵器 2024-10-02 09:45:23

您只需将十六进制数除以可能的最大值即可得到正确的比率:

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

You can just divide the hex number by the maximum possible to get the correct ratio:

>>> float(0x9481ef80) / 0x100000000
0.58010765910148621
风吹雪碎 2024-10-02 09:45:23

为了以足够的精度(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

请注意,浮点数使用“IEEE 双精度格式”,因此它只能保存 53 位精度:

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

如果将小数部分存储为自己的变量就可以了,但如果是这样,为什么不保持原样呢?

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

To represent the sum of integral and fractional part with enough precision (32 + 29 = 61 bits), you need a Decimal (28 decimal digits by default, which is enough for 93 bits),

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

or Fraction (exact),

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

Note that a float uses "IEEE double format" so it can only hold 53 bits of precision:

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

It is fine if you store the fractional part as its own variable, but if that's the case, why not just keep it as-is?

>>> 0x9481ef80 / 2**32
0.5801076591014862
>>> 0x9481ef90 / 2**32
0.5801076628267765
赠我空喜 2024-10-02 09:45:23

你没有说从什么时候开始的秒数。看起来是从 1970 年 1 月 1 日开始的。您可以计算一个模糊因子,即纪元 (1970-01-01) 与您预期的最低值之间的秒数。然后调整每个值... vadj = float(hi32 - fudge) + lo32 / 2.0 ** 32

如果 max(hi32) 和 min(lo32) 之间的差异小于大约 6 天的值(对于数据包捕获练习来说应该足够了(?)),那么 hi32 只需要 19 位 - 软糖。 19 位 + 32 位等于 51 位——在 Python 浮点 IIRC 的精度范围内。

现在已经很晚了,所以我不打算进行详细分析,但上面的内容应该可以让您了解情况。

编辑:为什么@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 位)"""。请提供该文档的 URL。

一秒有 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 的两倍。请解释一下。 “翻译成”声明的来源是什么?你还有其他例子吗?

You didn't say seconds since when. It looks like it's since 1970-01-01. You can calculate a fudge factor that is the number of seconds between the epoch (1970-01-01) and your expected lowest value. Then you adjust each value ... vadj = float(hi32 - fudge) + lo32 / 2.0 ** 32

If the difference between max(hi32) and min(lo32) is less than about 6 days worth (should be enough for a packet capture exercise (?)), then you need only 19 bits for hi32 - fudge. 19 bits + 32 bits is 51 bits -- within the precision of a Python float IIRC.

It's late here so I'm not going to do a detailed analysis but the above should give you the picture.

Edit: why @unwind's answer doesn't work:

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

Edit 2: What operations do you want to do on a timestamp apart from str(), repr(), timestamp_from_str()? Difference is about all that comes to mind. You can use something like this:

>>> 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'
>>>

About my "if you must" comment: If the observations are more than 6 days apart, do you really need accuracy down to the last (second / 2 ** 32)??? IMHO, if you do float(difference(ts1, ts2)) instead of float(ts1) - float(ts2), you should be OK.

Edit 3: Ambiguity/inconsistency alert

Please edit your question to address the following issues:

You say in a comment that """the documentation I'm looking at says that it the fractional part has nano-second precision (specifically it outputs 29 of the 32 bits)""". Please provide a URL for that documentation.

There are 1000000000 (10**9) nanoseconds in a second. One would expect the fractional part to require math.log(10**9, 2) rounded up (i.e. 29.897352853986263 rounded up i.e. 30) bits, not 29. Please explain.

Please answer: Of the 32 bits available, which 29 or 30 bits contain the fractional part and which 3 or 2 bits are always zero?

Secondly one would expect to convert the nanoseconds to seconds by dividing by 10**9. However your statement in your question """the number 4ca1f350 9481ef80 translates to 1285682000.580107659""" is consistent with dividing by 2**32. In fact 0x9481ef80 is 2,491,543,424 which is greater than twice 10**9. Please explain. What is the source of the "translates to" statement? Do you have any other examples?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文