为各种事物实现数学函数非常简单。 int mul(int,int);
,int pow(int,int);
,偶数double div(float,float);
很容易做到,可以通过循环或递归来实现。 (这些是用于手动或在头部执行这些功能的相同方法。)要相乘,只需重复添加数字即可。划分,重复减去它。要获得力量,反复倍增。等等。
然而,我一直想知道的一个数学函数是根。例如,您如何编写函数来计算数字的平方(或多维数据集等)根(即double root(float num, float root);
)?我试着环顾四周,找不到这样做的算法或方法。
当我尝试手动计算根时,我通常使用猜测方法(从一个近似数字开始,添加一个分数,乘以,看它有多远,加一个小分数,乘以,再次检查,和重复,直到满意)。我想这可行,但肯定有更好更快的方法(无论计算机能做多快多于手工操作)。
显然LUT不相关,因为它必须足够通用才能占用任何操作数(除非你用一组有限的数据编写游戏)。 Wikipedia article提到了猜测方法并列出了一些古老的方法(早在计算机发明之前)以及一些纯数学甚至微积分方法(包括一些以“无限”作为组件的方法)。唯一似乎与电子产品有关的方法是使用技巧或逻辑。 (这只是 square-roots ,更不用说立方根了,等等。)
没有简单的根计算方法吗?计算器如何做到这一点?电脑怎么做? (不,仅仅执行double pow(a,0.5);
将无效,因为double pow(float,float)
将如何实施?)
我是否只是错误地将根函数分组为更简单的函数?它们比它们看起来更复杂吗?
答案 0 :(得分:3)
有几种可能性。有两种不同的迭代方法,例如二分法或牛顿法。就使用pow
而言,有些计算机(例如x86)有一条指令要做(至少部分)将数字提升到一个幂,所以这纯粹是在编写一些框架的问题。
这是一个用于平方根的牛顿方法的汇编语言实现,在这种情况下只使用16位整数,但同样的基本思想适用于其他类型。我在大约20年前写过这篇文章,因此它适用于没有浮点硬件的16位CPU。
isqrt proc uses di, number:word
;
; uses bx, cx, dx
;
mov di,number
mov ax,255
start_loop:
mov bx,ax
xor dx,dx
mov ax,di
div bx
add ax,bx
shr ax,1
mov cx,ax
sub cx,bx
cmp cx,2
ja start_loop
ret
isqrt endp
这是x87计算任意功率的一些代码:
pow proc
; x^y = 2^(log2(x) * y)
fyl2x
fld st(0)
frndint
fld1
fscale
fxch st(2)
fsubrp
f2xm1
fld1
faddp
fmulp
ret
endp
但请注意,您通常不希望通过重复添加来实现乘法,或者通过重复减法来实现除法。相反,你想要移动并加/减两个连续的幂来更快地得到结果。
以下是一些显示一般概念的代码:
mult proc
; multiplies eax by ebx and places result in edx:ecx
xor ecx, ecx
xor edx, edx
mul1:
test ebx, 1
jz mul2
add ecx, eax
adc edx, 0
mul2:
shr ebx, 1
shl eax, 1
test ebx, ebx
jnz mul1
done:
ret
mult endp
这对x86来说毫无意义,因为它内置了乘法指令,但在较旧的处理器(PDP-11,8080,6502等)上,这样的代码相当常见。
答案 1 :(得分:0)
您可以直接从您声明的文章中直接链接N-th root algorithm。它来自Newton's method。
答案 2 :(得分:0)
这取决于你想要的一般程度。例如,如果要计算(-4.2) 0.23 ,则需要复杂的算术运算。正如Mat指出的那样,对于整数n和正x,存在用于计算x 1 / n 的快速算法。如果你想要x y 为正x和任何y,那么log和指数就可以工作。