给定两个正浮点数x和y,如果除法运算符,如何将x / y计算到指定容差e内? 不能用?
您不能使用任何库函数,例如log和exp;加成 和乘法是可以接受的。
我可以知道如何解决它?我知道解决除法的方法是使用按位运算符,但在这种方法中,当x小于y时,循环停止。
def divide(x, y):
# break down x/y into (x-by)/y + b , where b is the integer answer
# b can be computed using addition of numbers of power of 2
result = 0
power = 32
y_power = y << power
while x >= y:
while y_power > x:
y_power = y_power>> 1
power -= 1
x = x - y_power
result += 1 << power
return result
答案 0 :(得分:1)
一个选项是使用Newton-Raphson迭代,已知会以二次方式收敛(这样精确位的数量将增长为1,2,4,8,16,32,64)。
首先使用迭代计算y
的倒数
z(n+1) = z(n) (2 - z(n) y(n)),
收敛后形成产品
x.z(N) ~ x/y
但挑战在于找到一个好的起始近似值z(0)
,它应该在2
的因子1/y
范围内。
如果上下文允许,您可以直接使用浮点表示的指数,并将Y.2^e
替换为1.2^-e
或√2.2^-e
。
如果禁止此操作,您可以提前设置一个包含2的所有可能功率的表格,并执行二分搜索以在表格中找到y
。然后在表中很容易找到逆功率。
对于双精度浮点数,有11
个指数位,因此权力表应保持2047
个值,这可以被认为是很多。您可以通过仅存储指数2^0
,2^±1
,2^±2
,2^±3
来交换存储计算...然后在二分法搜索期间,您将重新创建中间指数通过产品(即2^5 = 2^4.2^1
)的需求,同时形成逆的产物。这可以通过仅lg(p)
倍增来有效地完成,其中p=|lg(y)|
是所需的功率。
示例:查找1000
的权力;指数用二进制表示。
1000 > 2^1b = 2
1000 > 2^10b = 4
1000 > 2^100b = 16
1000 > 2^1000b = 256
1000 < 2^10000b = 65536
然后
1000 < 2^1100b = 16.256 = 4096
1000 < 2^1010b = 4.256 = 1024
1000 > 2^1001b = 2.256 = 512
这样
2^9 < 1000 < 2^10.
现在Newton-Raphson迭代产生
z0 = 0.001381067932
z1 = 0.001381067932 x (2 - 1000 x 0.001381067932) = 0.000854787231197
z2 = 0.000978913251777
z3 = 0.000999555349049
z4 = 0.000999999802286
z5 = 0.001
答案 1 :(得分:0)
可能最直接的解决方案是使用Newton's method for division计算倒数,然后可以乘以分子得到最终结果。
这是一个迭代过程,逐渐完善初始猜测,并在每次迭代时加倍精度,并且只涉及乘法和加法。
一个复杂因素是产生合适的初始猜测,因为不正确的选择可能无法收敛或需要更多次迭代才能达到所需的精度。对于浮点数,最简单的解决方案是对2的幂指数进行标准化,并使用1
作为初始猜测,然后分别反转并重新应用指数以获得最终结果。这产生大约2^iteration
位的精度,因此对于具有53位尾数的典型IEEE-754双精度,6次迭代应该足够了。
然而,考虑到中间计算的有限精度,将结果计算到绝对容错e
内是困难的。如果指定太紧,则可能无法表示,更糟糕的是,最小半ULP限制需要精确算术。如果是这样,您将被迫手动实现相当于精确的IEEE-754除法功能,同时非常注意舍入和特殊情况。
以下是C中的一种可能的实现方式:
double divide(double numer, double denom, unsigned int precision) {
int exp;
denom = frexp(denom, &exp);
double guess = 1.4142135623731;
if(denom < 0)
guess = -guess;
while(precision--)
guess *= 2 - denom * guess;
return ldexp(numer * guess, -exp);
}
对零,其他非正规,无穷或NaN等特殊情况的处理和分析留给读者练习。
frexp
和ldexp
库函数很容易替代指数和尾数的手动位提取。但是这很麻烦且不可移植,并且在问题中没有指定特定的浮点表示。
答案 2 :(得分:0)
首先,您应该从两个数字中分隔符号和指数。在那之后,我们将划分纯正的尾数,并使用前指数和符号来调整结果。
至于划分尾数,很简单,如果你记得那个除法不仅是倒数乘法,而且还要做多次减法。结果是次数。
A:B->C, precision e
C=0
allowance= e*B
multiplicator = 1
delta = B
while (delta< allowance && A>0)
if A<delta {
multiplicator*=0.1 // 1/10
delta*=0.1 // 1/10
} else {
A-=delta;
C+=multiplicator
}
}
真的,我们可以使用任意数字&gt;而不是10.这将是有趣的,这将提供最有效的。当然,如果我们使用2,我们可以在循环内使用shift而不是乘法。