C中的定点无符号除法
Posted
技术标签:
【中文标题】C中的定点无符号除法【英文标题】:Fixed-point unsigned division in C 【发布时间】:2011-12-14 14:43:18 【问题描述】:我需要一个算法来在 C 中进行无符号定点除法。我最多可以使用 32 位字。
我想尽量减少表示整数部分所需的位数,同时能够使用 [0..15] 范围内的数字。显然,最小位数是 4。问题是我提出的算法只能使用 5 位。因为它将余数与除数进行比较,然后移动余数直到它大于除数,如果除数的最高有效位为 1,那么算法只会移动余数(它永远不会更大)。代码如下:
int divu(int a, int b)
int pt_int, r, pt_frac=0;
int i;
pt_int = ((unsigned) a/b) << BITS_FRAC;
r = (unsigned) a%b;
for (i=BITS_FRAC; i>=0; i--)
if ((unsigned) r < b)
r <<= 1;
else
r -= b;
pt_frac += 01 << i;
r <<= 1;
return pt_int + pt_frac;
如果您确实有解决方案但不想理解代码,请发布它。 :)
例子:
我们想将 1.5 除以 2,得到 0.75。假设我们对整数部分使用 4 位,对小数部分使用 28 位。所以我们的数字是十六进制的:
1.5: 0x18000000
2: 0x20000000
result: 0x0c000000
【问题讨论】:
我不明白这个问题。您能否提供示例输入以及预期和实际输出。 如果你想除以无符号值,你至少可以在你的函数声明中这样说。 您的代码看起来不像处理被零除或溢出。例如,如果你将 4 除以 0.25,就会出现问题。 @David Hefferman 是的。这是家庭作业。但至少我正在努力做到这一点。 @unwind 很抱歉。在我将要使用的应用程序中,它必须是这样的,我只是粘贴了我编写的代码。我会编辑它。 【参考方案1】:你有一个 4.28 的定点数,你想除以一个 4.28 的数。您可以通过从分母中减去分子的精度来找到除法后的精度,因此直接除法将给出 4.28 - 4.28 = 0 - 没有有效位。显然这是行不通的。
1.5 [4.28] 0x18000000
/ 2.0 [4.28] 0x20000000
= 0? [0.00] 0x00000000
理想的方法是将分子提升到 8.56(乘以 2^28),然后进行 64 位除法:
. . . .
1.5 [8.56] 0x180000000000000
/ 2.0 [4.28] 0x20000000
= 0.75 [4.28] 0x0c000000
如果您真的不能使用 64 位数字,那么您唯一的选择就是减少分母。 例如,您可以通过除以 2^14 来使用一半的精度
1.5 [4.28] 0x18000000
/ 2.0 [2.14] 0x8000
= 0.75 [2.14] 0x3000
然后您可以将结果乘以相同的因数得到 4.28 数字:0x3000 *(1<<14) = 0x0c000000
这样做确实会损失一些精度,但如果不使用更大的分子,这是不可避免的。例如
5.0/3.0 = 1.66667 = 0x1AAAAAA [4.28]
,但是((5.0<<28)/(3<<14))<<14 = 0x1AAA8000 [4.28] = 1.66662
【讨论】:
我想还是按我的方式做比较好。整数部分不使用 5 位的目的是避免精度损失,您算法的损失大于我的 1 位。 你的更准确,它也很慢。定点的通常动机是具有慢浮点数学的处理器上的速度。但如果你想要准确性,你的方法是最好的。我认为至少丢失一位是不可避免的。【参考方案2】:正如here 指出的那样(示例:将整数乘以常数的倒数),您可以通过乘以倒数来重新实现除法。之后你应该可以用 4 位来表示整数部分。
【讨论】:
您的链接是否使用全精度执行逆和乘法?在这种情况下,这将不起作用 - 您的方法将“正确”答案四舍五入,而不是执行定点除法。这两种方法通常会给出不同的结果。以上是关于C中的定点无符号除法的主要内容,如果未能解决你的问题,请参考以下文章