版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖。如要转贴,必须注明原文网址
了解了浮点数的存储以及手算平方根的原理,我们可以考虑程序实现了。
先实现一个64位整数的平方根,根据之前的手算平方根,程序也不是那么难写了。
//找到最高位的1,并且产生平方根结果最高位的1
//根据手算平方根的原理,依次产生各位结果
//右移动两位,并把a接着的两位并入remain
其实,可以合在一起写,代码会短一些,但效率会低那么一点点,而且编译器应该不太容易优化。
不过,我们不需要这个结果。
为了验证其正确性,我们来写个C语言的main
我们shell程序测试一下,我们当然不可能测试过每一个64bits的数,这个运算量太大,不现实。我们可以用随机取一部分来测试。
#如果不是0,则代表要产生的数二进制可以有多少位
#产生一个bits位的二进制数x
#用bc将x转换成十进制
#用bc计算x的平方根取整,理论上和我们的C语言计算一致
#z是我们的C语言计算结果
#比较,如果不一致,就报错
测试结果表明,我们的C语言还是可以得到正确的结果的。
再来回忆下第一节里讲过的浮点数结构,
所以此处要用a或者2*a来开平方根,
回忆一下浮点数的结构,单精度浮点数的精度是23位。
表示的是科学计数法a*2的a减去1的部分,那么加上整数1可以用二进制24位表示。
于是,我们就想,一个二进制48位或47位长的数,平方根是二进制24位。那么,我们就可以用一个48位或47位的二进制整数的平方根计算结果的小数部分。
-0.0的平方根是-0.0(可能只是某些库里是这样的),
以上都可以在计算的时候特殊化一下。
规格数(就是用科学计数法表示的浮点数)的平方根也是规格数,
我们稍微计算一下,可以明白,所有的此类数的平方根都在规格数表示的范围内。
于是,有了以下的代码。
//然后需要移位,要区分奇数和偶数
同样,也写个测试用的程序,对inf/-inf/nan/0.0/-0.0以及负数不测了,这些很简单。
结果发现,我们的程序和数学库里的sqrtf结果有细微差别。
于是,我们决定再加个小东西,就是四舍五入。之前我们用的是47位或者48位数开平方,为了四舍五入,我们需要多一位,于是就用49位或者50位数开平方。
修改一下mysqrtf,增加两位拿去开平方,_sqrt_也动一下。
//然后需要移位,要区分奇数和偶数
然后再测,准确无误。于是我们可以完工了。