各种编程语言是如何实现sqrt?
上面说了3个解法,那你是不是也很好奇目前各种编程语言库函数里对sqrt是如何实现的? 我也很好奇,于是我们帮你们翻了jdk源码,发现它根本就不需要自己实现sqrt,毕竟sqrt在计算机领域是有个比较常用的计算,所以主流的CPU架构都提供了对sqrt的硬件支持,只需要一条汇编指令就可以了,在x86架构下sqrt可以直接用下面这条汇编指令。
sqrtsd %1, %0" : "=x" (res) : "xm" (x)
在Risc-v中可以可以用fsqrt.s或fsqrt.d指令,Rics-v中文手册
硬件可以在一个指令周期内完成一个数的开方,相比那些需要几十甚至成百上千个CPU指令实现的各种算法而言,这速度差异显而易见。 实际上上,现代CPU在多核心、流水线、多发射、超标量……等技术的加持下,普通家用CPU都可以做到每秒百亿次的浮点运算。
生活处处有惊喜,当我打开python math模块的源码时,没有发现浮点数的求根(估计也是直接用的CPU级指令),但我发现了一个更骚的对64位整数求根的操作,所以这里再补充介绍一个python的近似求根算法。
python中的_approximate_isqrt()
下面这段代码可以返回输入值求根后的整数部分,但完全不知道是什么原理。
_approximate_isqrt(uint64_t n) { uint32_t u = 1U + (n >> 62); u = (u << 1) + (n >> 59) / u; u = (u << 3) + (n >> 53) / u; u = (u << 7) + (n >> 41) / u; return (u << 15) + (n >> 17) / u; }
源码中有注释,这段C代码可以翻译为以下python代码,不过我依旧看不懂。
def isqrt(n): """ Return the integer part of the square root of the input. """ n = operator.index(n) if n < 0: raise ValueError("isqrt() argument must be nonnegative") if n == 0: return 0 c = (n.bit_length() - 1) // 2 a = 1 d = 0 for s in reversed(range(c.bit_length())): # Loop invariant: (a-1)**2 < (n >> 2*(c - d)) < (a+1)**2 e = d d = c >> s a = (a << d - e - 1) + (n >> 2*c - e - d + 1) // a return a - (a*a > n)
结语
这篇博客从立题到完成经历了好几天的时间,期间整理思路、编码、绘图、查阅资料、修改完善总累计耗时近8h。写作不易,如果文章对你有用欢迎素质三连(点赞、收藏加关注) 。
引用链接
[1] 牛顿迭代法: https://baike.baidu.com/item/%E7%89%9B%E9%A1%BF%E8%BF%AD%E4%BB%A3%E6%B3%95/10887580?fr=aladdin
[2] 卡马克快速平方根倒数算法: http://jcf94.com/2016/01/14/2016-01-14-carmack/
[3] Fast Inverse Square Root: http://read.pudn.com/downloads203/sourcecode/game/955182/3D%20Geometry%20Tuts/FastInverseSqrt.pdf
[4] From 百度百科: https://baike.baidu.com/item/0x5f375a86/10449453?fr=aladdin
[5] 这条汇编指令: https://code.woboq.org/userspace/glibc/sysdeps/x86_64/fpu/e_sqrt.c.html
[6] Rics-v中文手册: http://crva.ict.ac.cn/documents/RISC-V-Reader- Chinese-v2p1.pdf
[7] python math模块: https://github.com/python/cpython/blob/master/Modules/mathmodule.c