如何在没有较大中间类型的情况下对整数进行乘法和除法?

时间:2016-11-29 20:28:01

标签: c# algorithm performance division multiplication

目前,我正在开发一些C#中的模糊逻辑内容,并希望以通用的方式实现这一点。为简单起见,我可以使用floatdoubledecimal来处理区间[0,1],但是为了提高性能,最好使用整数。关于对称性的一些想法也导致决定省略无符号中的最高值和有符号整数中的最低值。最低的非省略值映射为0,最高的非省略值映射为1.省略的值归一化为下一个未省略的值。

现在,我想以下列形式实现一些复杂的计算:

byte f(byte p1, byte p2, byte p3, byte p4)
{
    return (p1 * p2) / (p3 * p4);
}

其中字节值被解释为上面提到的[0,1]间隔。这意味着p1 * p2 < p1p1 * p2 < p2而不是大于1的数字,这是无效的,例如: G。 2 * 3 = 6,但0.1 * 0.2 = 0.02

此外,问题是:p1 * p2p3 * p4可能会超出byte类型的范围。整个公式的结果可能不会超出此范围,但溢出仍会在一个或两个部分中发生。当然,我可以直接转到ushort,最后回到byte,但对于ulong,如果没有进一步的努力,我就不会有这种可能性,我不想坚持32位。另一方面,如果我return (p1 / p3) * (p2 / p4),我会减少类型升级,但可能会遇到0的结果,其中实际结果为非零。

所以我想到了以某种方式同时“缩小”两种产品,直到我得到[0,1]解释的结果。我不需要一个精确的值,一个错误小于3个整数值的启发式关闭正确的值就足够了,对于ulong一个更高的错误肯定是可以的。

到目前为止,我已尝试将输入转换为区间[0,1]中的decimal / float / double并进行计算。但这对性能完全适得其反。我读过有关division algorithms的内容,但我找不到我在课堂上看过的那个。它是关于使用累加器同时计算商和余数。我试图通过校正来重构和扩展它的分解的分解部分,但是这会破坏,在不可分割的情况下发生并且我得到一个太大的错误。我还做了一些笔记并手动计算了一些整数示例,尝试分解,取消,拆分和这些花哨的派生事物,但没有任何结果导致令人满意的结果或算法的步骤。

有没有

  • 高效的方式
  • 如上所述,对有符号(和无符号)整数进行乘法/除法
  • 解释为区间[0,1]
  • 没有类型宣传

2 个答案:

答案 0 :(得分:0)

如果你不小心,你将失去更多时间进行常规操作所需的转换。

话虽如此,一个可能有意义的替代方案是将0到128之间的值映射(如果你想要更高的精度,则为0和32768),以便所有值基本上都存储乘以128

因此,如果您有(0.5 * 0.75) / (0.125 * 0.25),则每个数字的存储值将分别为64,96,16和32。如果您使用ushort进行计算,则会(64 * 96) / (16 * 32) = 6144 / 512 = 12。这会产生12 / 128 = 0.09375的结果。

顺便说一下,你可以忽略加法,减法和除法的缩放。对于乘法,你会像往常一样进行乘法,然后除以128.因此对于0.5 * 0.75,你会64 * 96 / 128 = 48符合预期的48 / 128 = 0.375

可以针对平台优化代码,特别是如果平台对于较窄的数字更有效。如有必要,可以在操作中添加舍入。

顺便提一下,如果功率为2,则可以使用位移进行缩放。您可能更喜欢使用256而不是128,特别是如果您没有一个循环位移,但是您需要更大的宽度来处理某些操作。

但是如果没有设置最重要的位,那么你可以做一些优化,这样你只需要在必要时使用更大的宽度。

答案 1 :(得分:0)

总结回答你的问题:不。
您需要明确说明(并排名)您的总体目标(例如,对称性能更重要还是更重要?)。在问题中简明扼要地陈述,你获得有用答案的机会有所改善 虽然我认为Phil1970's you can ignore scaling for … division过于乐观,但乘法就足够了:如果你没有产生比“基本类型”更大的部分结果(两倍大),你就会陷入操作数和拼接的倍增部分结果一起。
关于拼凑“更大”结果的想法:AVR's Fractional Multiply 关于…in signed integers. The lowest, non-omitted value maps to 0…,我希望您会发现,例如多余的-32767 / 32768编码分数比两个补码更难处理。