我正在尝试将原始二进制数据从线程上下文转换为人类可读的格式,并且在尝试将quadruple-precision floating point值转换为C#中的可读格式时出现空白。
最后,我想用标准科学记数法显示它,例如1.234567×10 89 。我并不担心过程中精度的损失 - 我只是想要了解价值是什么。
我的第一个想法是通过提高指数手动计算值为double,但当然在很多情况下我会超过最大值。我不介意失去精确度,但根本无法显示它。
我可以使用某种简单的数学黑客吗?
答案 0 :(得分:2)
所以,这是扩展我之前发表的评论的答案。我希望你不要介意我使用Python,因为我知道在哪里可以找到我需要的语言;也许其他人可以在C#中将其翻译成合适的答案。
假设您有一个128位的序列表示IEEE 754 binary128格式的数字,并且我们当前以无符号整数x
的形式读取这些128位。例如:
>>> x = 0x4126f07c18386f74e697bd57a865a9d0
(我想在C#中会有点麻烦,因为据我所知它没有128位整数类型;你需要使用两个64位整数对于高低字,或使用BigInteger
类型。)
我们可以像往常一样通过位操作提取指数和有效数字(我假设你已经达到了这个目的,但我想包括计算的完整性):
>>> significand_mask = (1 << 112) - 1
>>> exponent_mask = (1 << 127) - (1 << 112)
>>> trailing_significand = x & significand_mask
>>> significand = 1.0 + float(trailing_significand) / (2.0**112)
>>> biased_exponent = (x & exponent_mask) >> 112
>>> exponent = biased_exponent - 16383
请注意,虽然指数是精确的,但此时我们已经失去了significand
的大部分精度,只保持52-53位的精度。
>>> significand
1.9393935334951098
>>> exponent
295
因此,所代表的值大约为1.9393935334951098 * 2**295
,或大约为1.234567e+89
。但是你不能直接在这个阶段进行计算,因为它可能溢出Double
(在这种情况下它不会,但如果指数更大,你就会出现问题) 。所以这里是日志的来源:让我们计算x
所代表的值的自然对数:
>>> from math import log, exp
>>> log_of_value = log(significand) + exponent*log(2)
>>> log_of_value
205.14079357778544
然后我们可以除以log(10)
得到小数部分的指数和尾数:除法的商给出十进制指数,而余数给出有效数的 log ,所以我们必须对它应用exp
来检索实际的有效数字:
>>> exp10, mantissa10 = divmod(log_of_value, log(10))
>>> exp10
89.0
>>> significand10 = exp(mantissa10)
>>> significand10
1.234566999999967
很好地格式化答案:
>>> print("{:.10f}e{:+d}".format(significand10, int(exp10)))
1.2345670000e+89
这是基本思想:通常,您还需要处理符号位和零,次正规数,无穷大和NaN的特殊位模式。根据应用程序的不同,您可能不需要所有这些。
在将整数有效数转换为双精度浮点数时,首先涉及一些精度损失,但在获取日志和指数时也是如此。当指数很大时,精度损失的最坏情况发生,因为大指数放大了log(2)
计算中涉及的绝对误差,这在采用exp
获得最终结果时会产生更大的相对误差。尾数。但由于(无偏)指数不超过16384,因此不容易限制误差。我还没有完成正式计算,但这对于binary128
格式范围内的大约12位精度应该是好的,对于小指数的数字,精度应该更好一些。
答案 1 :(得分:2)
您可以安装处理该问题的第三方库。例如,QPFloat似乎为您提供了一个名为struct
的新System.Quadruple
,它会覆盖ToString
,因此您可以尝试这样做。
(我想知道.NET何时支持System.Quadruple
。)
答案 2 :(得分:1)
很少有黑客......
计算数字的十六进制字符串
尾数和指数是二进制的,所以应该没有问题,不要忘记为每个2^4
指数部分添加零,并将尾数移动exponent&3
位。负指数需要很少的调整,但非常相似。
所有这一切都可以通过位和移位操作完成,因此如果编码正确则不会出现精度损失...
将十六进制字符串转换为十字字符串
这里也有很多例子,SO here是我的。你也可以稍微调整它以跳过零处理以获得更快的速度......
现在扫描dec字符串
如果您查看上面链接中的我的dec2hex
和hex2dec
次转化,则您需要找到扫描:
从这些中你可以轻松计算指数
将dec字符串转换为尾数* 10 ^ exponet表格
它非常直接只需删除零...并将小数点转换为新位置然后添加指数部分......
为尾数添加符号
你可以直接在子弹#1,#2 中添加它,但是如果你最后这样做,它会为你节省一些if
...
希望这会有所帮助......