【问题标题】:How to multiply double (floating point) by an integer type (32-bit, 64-bit, 128-bit, etc.) manually如何手动将双精度(浮点数)乘以整数类型(32 位、64 位、128 位等)
【发布时间】:2020-04-24 11:02:28
【问题描述】:

我正在尝试手动实现双精度和 128 位整数之间的乘法,这是我使用两个 ulong 创建自己的。

我的理解如下:
1. 将双精度数分解为有效数和指数。确保有效数字是normalized
2. 将有效数字与我的 uint128 相乘。这会给我 256 位数。
3. 将我的 256 位数按从双精度中提取的指数移位。
4.如果值超过128位,那么我溢出了。

我觉得我非常接近,但我错过了一些东西。可以说我有以下示例。我正在存储一个值为 2^127 的 uint128,我想将它乘以 8E-6。

uint128 myValue = new uint128(2^127);
double multiplier = 8E-6;
uint128 product = myValue * multiplier;

真正的价值或正确答案是1361129467683753853853498429727072.845824。 所以我想将值 1361129467683753853853498429727072 作为我的 128 位整数。

问题是我的实现给了我1361129467683753792259819967610881

int exponent; // This value ends up being -69 for 8E-6
uint128 mantissa = GetMantissa(multiplier, out exponent); // This value ends up being 4722366482869645 after normalizing it.
uint256 productTemp = myValue * mantissa; // This value is something like 803469022129495101412490705402148357126451442021826560.
uint128 product = productTemp >> exponent. // this value is 1361129467683753792259819967610881

我正在使用来自extracting mantissa and exponent from double in c# 的代码来获取我的尾数和指数。我可以使用这些值正确地将 8E-6 作为双精度返回。

有人知道我在这里做错了什么吗?如果我使用 .8 而不是 8E-6,我的值会更好。

【问题讨论】:

  • 你在这里展示的是什么编程语言?
  • C# 是我正在编写的,但只要使用 IEEE 754 浮点双精度,该语言就不太相关。我写了我自己的 UInt128,我自己处理 256 位的乘法,以及双精度的分解。
  • 退一步看大局,我知道您已经有了一个可以得到您想要的结果的数据类型,System.Decimal,但您希望获得更好的性能。通常,最大的一步是在硬件实现和软件实现之间。你确定你会获得足够的性能提升来证明这项工作的合理性吗?
  • @PatriciaShanahan 我不是。 System.Double 的性能明显快于 System.Decimal 的性能,但 System.Double 可能不足以满足我需要的精度。我正在查看针对特定值范围的一组特定操作。我认为最终,我不会更快,但我需要有有效的代码来进行基准测试,以便自信地展示它。

标签: math floating-point double


【解决方案1】:

我在这里做错了什么?

double multiplier 没有算术值 0.000008。它有一个dyadic value 接近 0.000008,到 15-17 个有效小数位。这种差异导致没有达到您的期望。

1234567890123456
1361129467683753 853853498429727072.845824 - perceived product
1361129467683753 853853498429727072        - perceived rounded product
1361129467683753 792259819967610881        - product seen.

尝试 multiplier 使用 精确 十进制值,例如 0.0625 (1.0/16)。


注意事项:

对于 binary64,最接近 8E-6 的 double 是 (@Patricia Shanahan) 0.000007999999999999999637984894607090069484911509789526462554931640625。

乘以 2127 正好是

1361129467683753 792259819967610880.0

所以乘法似乎是一倍的,也许是四舍五入?

【讨论】:

  • 有趣。所以有一些值,比如 0.8,结果如我所料。有没有办法可以处理没有算术值的双精度数,如果是这样,有没有一种方法可以判断双精度数是否有算术值?
  • @ChrisDelpire 唯一没有算术值的双精度数是无穷大和 NaN(所有位都是一个指数)。问题是反过来的。有许多小数不完全对应任何双精度,因此它们在小数到双精度转换时得到近似。
  • @PatriciaShanahan 那么这是否意味着当我存储 8E-6 时,它实际上是在引擎盖下存储 7.999999E-6?那么,例如在 C# 中,当我查看该值时,它是如何显示为 8E-6 的?
  • 最接近 8E-6 的 IEEE 754 64 位二进制数是 0.0000079999999999999999637984894607090069484911509789526462554931640625。大多数语言设计者认为程序员不希望看到这样的东西,所以他们缩短和舍入输出。
  • @ChrisDelpire 要获得 136...072,您需要将 2^127 乘以 double 以外的值 - 我怀疑 long double 也足够了。所以问题就变成了用什么类型/结构来表示8E-6
猜你喜欢
  • 2015-03-25
  • 1970-01-01
  • 2012-03-16
  • 2011-02-27
  • 1970-01-01
  • 2016-02-08
  • 1970-01-01
  • 2015-10-17
  • 1970-01-01
相关资源
最近更新 更多