在C#中以科学计数法显示IEEE-754四倍精度(二进制128)浮点值

时间:2014-04-07 16:45:38

标签: c# math floating-point ieee-754

我正在尝试将原始二进制数据从线程上下文转换为人类可读的格式,并且在尝试将quadruple-precision floating point值转换为C#中的可读格式时出现空白。

最后,我想用标准科学记数法显示它,例如1.234567×10 89 。我并不担心过程中精度的损失 - 我只是想要了解价值是什么。

我的第一个想法是通过提高指数手动计算值为double,但当然在很多情况下我会超过最大值。我不介意失去精确度,但根本无法显示它。

我可以使用某种简单的数学黑客吗?

3 个答案:

答案 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)

很少有黑客......

  1. 计算数字的十六进制字符串

    尾数和指数是二进制的,所以应该没有问题,不要忘记为每个2^4指数部分添加零,并将尾数移动exponent&3位。负指数需要很少的调整,但非常相似。

    所有这一切都可以通过位和移位操作完成,因此如果编码正确则不会出现精度损失...

  2. 将十六进制字符串转换为十字字符串

    这里也有很多例子,SO here是我的。你也可以稍微调整它以跳过零处理以获得更快的速度......

  3. 现在扫描dec字符串

    如果您查看上面链接中的我的dec2hexhex2dec次转化,则您需要找到扫描:

    • 左右第一个非零小数的位置
    • 小数点位置

    从这些中你可以轻松计算指数

  4. 将dec字符串转换为尾数* 10 ^ exponet表格

    它非常直接只需删除零...并将小数点转换为新位置然后添加指数部分......

  5. 为尾数添加符号

    你可以直接在子弹#1,#2 中添加它,但是如果你最后这样做,它会为你节省一些if ...

  6. 希望这会有所帮助......