如何在没有更大的中间类型的情况下对整数进行乘法和除法?
How can I multiply and divide integers without bigger intermediate types?
目前,我正在用 C# 开发一些模糊逻辑的东西,并希望以通用的方式实现它。为了简单起见,我可以使用 float
、double
和 decimal
来处理区间 [0, 1],但是为了性能,使用整数会更好。关于对称性的一些想法也导致决定省略无符号整数中的最高值和有符号整数中的最低值。最低的非省略值映射到 0,最高的非省略值映射到 1。省略的值被归一化为下一个非省略值。
现在,我想以以下形式实现一些复合计算:
byte f(byte p1, byte p2, byte p3, byte p4)
{
return (p1 * p2) / (p3 * p4);
}
其中字节值被解释为上面提到的 [0, 1] 区间。这意味着 p1 * p2 < p1
和 p1 * p2 < p2
而不是大于 1 的数字,在这种情况下这是无效的,例如。 G。 2 * 3 = 6
,但 0.1 * 0.2 = 0.02
.
另外,一个问题是:p1 * p2
和p3 * p4
可能超出类型byte
的范围。整个公式的结果可能不会超过这个范围,但仍然会在一个或两个部分发生溢出。当然,我可以转换为 ushort
,最后返回到 byte
,但是对于 ulong
,如果没有进一步的努力,我不会有这种可能性,我不想坚持32位。另一方面,如果我 return (p1 / p3) * (p2 / p4)
,我会减少类型升级,但可能 运行 变成 0
的结果,其中实际结果是非零的。
所以我以某种方式同时 "shrinking" 一步一步地想到这两种产品,直到我得到 [0, 1] 解释中的结果。我不需要一个确切的值,一个错误小于正确值 3 个整数值的启发式方法就足够了,对于 ulong
更高的错误肯定是可以的。
到目前为止,我已经尝试将输入转换为区间[0, 1]中的decimal
/float
/double
并进行计算。但这对性能完全适得其反。我阅读了有关 division algorithms 的内容,但找不到我在 class 中看到的内容。它是关于用累加器同时计算商和余数。我试图通过更正为除法的因式分解部分重建和扩展它,但这中断了,在不可分割性发生的地方,我得到了一个太大的错误。我还做了一些笔记并手动计算了一些整数示例,尝试分解、抵消、拆分和以及诸如此类花哨的推导,但没有任何结果或算法步骤令人满意。
有没有
- 高效的方式
- 到 multiply/divide 有符号(和无符号)整数,如上所述
- 解释为区间 [0, 1]
- 没有类型提升
?
如果您不小心,您将浪费更多时间进行常规操作所需的转换。
话虽这么说,一种可能有意义的替代方法是映射包含在 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,特别是如果您没有一个周期的位移位,但您需要更大的宽度来处理某些操作。
但是,如果未设置最高有效位,则您可以进行一些优化,例如,这样您就只会在必要时使用更大的宽度。
总结回答你的问题:否。
您需要明确地陈述(和排序)您的总体目标(例如,对称性 比 性能 更重要还是更不重要?在问题中简明扼要地说明它们,您获得有用答案的机会就会增加。
虽然我认为 过于乐观,但乘法已经足够成问题了:如果你没有生成比 "base type" 更大(两倍大)的部分结果,你就会被部分操作数相乘和将结果拼凑在一起。
有关拼凑 "larger" 个结果的想法:AVR's Fractional Multiply。
关于 …in signed integers. The lowest, non-omitted value maps to 0…
,我希望您会发现,例如 excess -32767/32768-coded fractions 比二进制补码更难处理。
目前,我正在用 C# 开发一些模糊逻辑的东西,并希望以通用的方式实现它。为了简单起见,我可以使用 float
、double
和 decimal
来处理区间 [0, 1],但是为了性能,使用整数会更好。关于对称性的一些想法也导致决定省略无符号整数中的最高值和有符号整数中的最低值。最低的非省略值映射到 0,最高的非省略值映射到 1。省略的值被归一化为下一个非省略值。
现在,我想以以下形式实现一些复合计算:
byte f(byte p1, byte p2, byte p3, byte p4)
{
return (p1 * p2) / (p3 * p4);
}
其中字节值被解释为上面提到的 [0, 1] 区间。这意味着 p1 * p2 < p1
和 p1 * p2 < p2
而不是大于 1 的数字,在这种情况下这是无效的,例如。 G。 2 * 3 = 6
,但 0.1 * 0.2 = 0.02
.
另外,一个问题是:p1 * p2
和p3 * p4
可能超出类型byte
的范围。整个公式的结果可能不会超过这个范围,但仍然会在一个或两个部分发生溢出。当然,我可以转换为 ushort
,最后返回到 byte
,但是对于 ulong
,如果没有进一步的努力,我不会有这种可能性,我不想坚持32位。另一方面,如果我 return (p1 / p3) * (p2 / p4)
,我会减少类型升级,但可能 运行 变成 0
的结果,其中实际结果是非零的。
所以我以某种方式同时 "shrinking" 一步一步地想到这两种产品,直到我得到 [0, 1] 解释中的结果。我不需要一个确切的值,一个错误小于正确值 3 个整数值的启发式方法就足够了,对于 ulong
更高的错误肯定是可以的。
到目前为止,我已经尝试将输入转换为区间[0, 1]中的decimal
/float
/double
并进行计算。但这对性能完全适得其反。我阅读了有关 division algorithms 的内容,但找不到我在 class 中看到的内容。它是关于用累加器同时计算商和余数。我试图通过更正为除法的因式分解部分重建和扩展它,但这中断了,在不可分割性发生的地方,我得到了一个太大的错误。我还做了一些笔记并手动计算了一些整数示例,尝试分解、抵消、拆分和以及诸如此类花哨的推导,但没有任何结果或算法步骤令人满意。
有没有
- 高效的方式
- 到 multiply/divide 有符号(和无符号)整数,如上所述
- 解释为区间 [0, 1]
- 没有类型提升
?
如果您不小心,您将浪费更多时间进行常规操作所需的转换。
话虽这么说,一种可能有意义的替代方法是映射包含在 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,特别是如果您没有一个周期的位移位,但您需要更大的宽度来处理某些操作。
但是,如果未设置最高有效位,则您可以进行一些优化,例如,这样您就只会在必要时使用更大的宽度。
总结回答你的问题:否。
您需要明确地陈述(和排序)您的总体目标(例如,对称性 比 性能 更重要还是更不重要?在问题中简明扼要地说明它们,您获得有用答案的机会就会增加。
虽然我认为
有关拼凑 "larger" 个结果的想法:AVR's Fractional Multiply。
关于 …in signed integers. The lowest, non-omitted value maps to 0…
,我希望您会发现,例如 excess -32767/32768-coded fractions 比二进制补码更难处理。