【问题标题】:What's the best multiplication algorithm for fixed point where precision is necessary什么是需要精度的定点的最佳乘法算法
【发布时间】:2016-07-08 04:07:44
【问题描述】:

我知道,我知道,人们可能会说“只是切换到浮点”,但由于我正在从事的项目的性质,目前这不是一个选项。我正在帮助用 C++ 编写编程语言,我目前很难获得一个非常准确的乘法算法,我有一个 VM,主要是 mod/smod、div/sdiv 的操作(即有符号的数字在这里不是问题), mul, 全小数的减半数和我乘除以创建我的移位的推送移位数。为简单起见,假设我正在使用 32 字节空间。我的算法几乎适用于涉及整数的任何事情,只是当我的小数部分超过 16 个字节时,我遇到了精度问题,如果我要对其进行四舍五入,这个数字会相当准确,但我希望它为尽可能准确,甚至愿意为它牺牲一点性能,只要它保持一个固定点并且不进入浮点领域。我关心的算法我会用一种伪代码来绘制。希望能深入了解如何让这件事变得更好,或者想知道为什么根据计算科学定律,我所要求的只是徒劳的努力。

对于完全小数(所有字节都是小数):

 A = num1 / halfShift //truncates the number down to 16 so that when multiplied, we get a full 32 byte num
 B = num2 / halfShift
 finalNum = A * B

对于大于 16 字节的其余数字,我使用此算法:

 this algorithm can essentially be broken down into the int.frac form
 essentially A.B * C.D taking the mathematic form of
 D*B/shift + C*A*shift + D*A + C*B
 if the fractional numbers are larger than the integer, I halve them, then multiply them together in my D*B/shift
 just like in the fully fractional example above

我应该注意某种“神奇”的舍入方法吗?请告诉我。

【问题讨论】:

  • 算法。这个词是“算法”。这是某人的名字。
  • A = num1 / halfShift //truncates the number down to 16 - 一旦你降低了输入的“精度”(分辨率,真的)(这里是 16,单位(字节/数字/位/字...)就更不重要了), 没有量的精确算术可以恢复/增加它。取而代之的是,选择您需要多少个“保护位置”(例如 2 个)并计算到所选精度(在这种情况下为 32+2=34)。在乘法的情况下,这允许丢弃几乎一半的部分乘积。四舍五入到最终精度。
  • @greybeard 你说的“守卫地方”是什么意思?
  • 您可能已经被打扰使用wikipedia on guard digits 或搜索引擎来查找What Every Computer Scientist Should Know About Floating-Point Arithmetic。 (我避免使用 guard bitsdigits 以免暗示基数。无论如何,命名不是标准化的:一位作者的 rounding bit 是另一位作者的第一个保护位。)(如果您希望使用浮点表示来为实际问题产生结果,请务必消化“戈德堡论文” - FP is evil魔法。)
  • (不客气(实际接受提示:-)。如果您希望有人没有发布问题(或者,如果有答案,答案)得到通知,请提及她的名字介绍@(你会得到建议)。)

标签: c++ algorithm multiplication fixed-point


【解决方案1】:

如果先进行乘法运算,然后进行缩放,您将获得最准确的结果。当然,这意味着您需要将乘法结果存储在 64 位 int 类型中。 如果这不是一种选择,那么您提前转移的方法是有意义的。但你肯定会失去精度。

无论哪种方式,如果您舍入而不是截断,您可以稍微提高准确性。

我支持 Aconcagua 建议的四舍五入。 为此,您需要在应用除法之前添加将被截断的最高位。

在你的情况下,看起来像这样:

A = (num1 + 1<<(halfshift-1)) >> halfshift 
B = (num2 + 1<<(halfshift-1)) >> halfshift
finalNum = A * B

编辑:

有关如何根据因子的值动态缩放因子和结果的示例(这提高了分辨率,从而提高了结果的准确性):

shiftA 和 shiftB 需要设置为使 A 和 B 各为 16 字节小数,因此 32 字节结果不会溢出。如果 shiftA 和 shiftB 事先不知道,可以通过统计 num1 和 num2 的前导零来确定。

A = (num1 + 1<<(shiftA-1)) >> shiftA
B = (num2 + 1<<(shiftB-1)) >> shiftB
finalNum = (A * B) >> (fullshift - (shiftA + shiftB))

【讨论】:

  • 嗯,我确实有能力先相乘并将其存储在某个地方......虽然不是真正的 64 位 int 类型......实际上要大得多,我正在使用默认的 big endian 256 位 int 类型。但是,我无法存储超过 256 位的任何内容,所以如果这是您推荐的,我不确定这是否可行。
  • 当我阅读你的问题时,它一定是字节错误。如果不能存储大于 32 字节的结果,则需要坚持使用 16 字节值作为因子。
  • 顺便说一句。我忘了提到,您还可以调整每次乘法的比例因子,以使结果不会溢出,然后对结果应用剩余的所需比例。根据数字,这可以大大提高分辨率。可能比通过适当的舍入得到的更多。
  • 当您对结果应用剩余的所需缩放比例时会是什么样子?
  • @RichardJohnCatalano:我已经更新了我的答案,包括一个关于动态比例因子和产品的示例。
【解决方案2】:

乘积的小数位数等于操作数中小数位数的总和。您必须执行乘法到该精度,然后根据所需的目标精度进行舍入或截断。

【讨论】:

  • 但是我猜什么是舍入的好方法?我对这一切都很陌生,我仍在试图了解什么是好的舍入算法。如果你能回答这个问题,我会给你答案。
  • 我建议四舍五入到最近。应该通过将最高丢弃位添加为最低未丢弃位来工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-22
  • 2011-02-23
  • 2020-03-03
相关资源
最近更新 更多