数值计算 --- 平方根倒数快速算法(上)

news2024/11/17 11:43:46

平方根倒数快速算法(上) --- 向Greg Walsh致敬!

写在最前面 --- 一场关于平方根倒数快速算法作者的讨论:

        上图中的这段代码出自一个早期的3D游戏<雷神之锤>的源代码,它实现的功能就是计算一个数x的平方根的倒数:

1/\sqrt{x}

        这段代码之所以称之为经典,私以为主要是因为以下几点原因:

                1,这段代码是出自于程序员大神John Carmack之手(实际上是另有其人:Greg Walsh)。

                2,这段代码之简洁,且计算速度非常快。

                3,这段代码中John Carmack遗留下来的那句锐评论“what the fuck”。这里,我把他翻译成“这是什么鬼?!!!”

                4,这代码中神秘的magic number“5F3759DF”究竟是干什么的?

        为了彻底的弄明白这些问题,比如说那个magic number是什么,怎么来的,有什么用?还有为什么这个算法的计算速度如此之快,精度如此之高,等等。我查阅了一些相关资料和视频。下面是我自己的总结,作为我对这段代码的学习备忘录。

        By the way:那有人要说了,这都2024年了,为什么还要研究这个算法。根据我自己的学习体会,我觉得主要就是兴趣。再其次就是这段代码从最初被创作到现已经过去这么多年了,被研究已经比较充分了。因此,在网上能够查到的相应资料很多,更有利于个人学习,再加上现在有了chatGPT的加持:)。当年很多不太清楚的地方,在后面这些年中也因为大家的钻研变得慢慢清晰了,尤其是这段代码的原作者(Greg Walsh)究竟是谁的争论,也是直到2007年随着大家的激烈讨论才逐渐浮出水面的!下面的这个名为Beyond3D的外国论坛记录了当年的讨论,以及这段代码的原作者出现的过程。


是John Carmack或Michael Abrash吗?

       在讨论之前提到的《Doom3》引擎中 NV40 的渲染路径时,这段代码被提到并归因于 John Carmack;他是显而易见的选择,因为它出现在他引擎的源代码中。Michael Abrash 也被认为可能是作者。Michael 以 x86 汇编优化专家的身份脱颖而出,他撰写了传奇的《Zen of Assembly Language》和《Zen of Graphics Programming》等书,并且在《Quake》开发期间与 John 一起工作,负责优化当时的 CPU 上的 Quake 软件渲染器。

        为了确定这段代码的究竟是不是他写的,有人写信问他,于是就有了下面的这段问答。(这一讨论发生在2004年)

上述回信的翻译:

你好,John,

Beyond3D.com 论坛上正在讨论这个问题,谁是以下代码的作者:

        这可以归因于你吗?分析显示它的原理极其巧妙,据说来自 Quake 3 的源代码。 大多数人说这是你的作品,也有人说是 Michael Abrash 的。你知道是谁写的吗?可以讲一下它的历史吗?

不是我,我也不认为是 Michael。也许是 Terje Mathison?

John Carmack


是Terje Mathisen吗? 

        尽管John Carmack在他的回信中否认自己是这段代码的作者,同时他也不认为这段代码出自Michael Abrash之手。但他提到了另一个可能的线索/可能的作者Terje Mathison,但随后针对这一问题的讨论便没了下文。

       直到最近(时间来了2005年的八月),John 在今年的 QuakeCon 演讲中提到完全开源 Quake 3 v1.32 源代码,包括 3D 渲染器,得到了在场观众的热烈欢呼。随着《Doom3》的发布并赢得了好评,黑客们开始关注 id 公司,问 Quake 3 这个相对较古老的渲染器什么时候可以供人们学习和使用。

        QuakeCon 后的一周,Quake 3 源代码被正式公开,Slashdot 也报道了这个显而易见的新闻,并再次提到了快速近似倒数平方根代码的作者问题。很快我想到了 Terje,但当时却忘了问他!

        Terje Mathisen是 x86 汇编语言优化领域的大师之一。回到 3D 图形的早期软件优化时代,像 Michael、Terje(当然还有 John)这样的人会花费大量时间对关键的性能代码进行手写汇编优化。这个投资在《Doom》和《Quake》的时代取得了显著的回报。你可以在 comp.lang.asm.x86 中看到 Terje 分享的建议、优化、轶事和代码片段,这个人不是一般的厉害。那么Terje是真正的作者吗?

上述回信的翻译: 

Ryszard 写道:

嘿 Terje,

自从 id 公司公开了 Quake 3 Arena 源代码后,这个问题再次出现。

你是写出这个快速倒数平方根实现的人吗?如果是,你能讲讲它的来历和你如何想出这个算法吗?一大批黑客和极客都想知道这个问题。由于 John 说不是他,可能也不是 Michael,那是你吗?

        你好,Ryszard,还有你好 John,好几年没见了。:-(

        谢谢你把我列为可能的作者,当我第一次看到这个问题时,我确实以为这是我写的代码。:-)

        五年前,我确实为了帮助一位瑞典朋友解决流体化学计算问题,写过一个非常快速且准确的invsqrt()函数。这使得他的模拟运行时间从原来的一周缩短到了一半,且能保证8-10位有效的数字。

        然而,文中的代码却不是我写的,我猜它的准确度应该只有百分之一以内?瑞典朋友需要至少 48 位有效位的精度,为此我采用了更直接的查表法和牛顿拉夫逊迭代法。由于水分子包含三个原子,我可以同时计算三个invsqrt()值,从而避免了 FP 流水线中几乎所有的气泡。

        不过,我感觉Q3A的代码风格更类似于MIT的HAKMEM文件中的一些代码。:-)

Terje


是Gary Tarolli吗?

        这样看来Terje也不是真正的作者,尽管如此,我们也在他的回信中看到了他的实力。他基于查表法和NR迭代法所写的invsqrt()汇编版的精度达到了惊人的48位。而且他回忆说,这些代码的风格类似于 MIT 的 HAKMEM 文档中的代码。

        随着John CarmackTerje Mathisen都否定了他们是平方根倒数快速算法这段代码的作者,且Michael Abrash也间接被否定了,看来要想找到原作者似乎需要另谋出路。通过 Google搜索,我们发现NVIDIA也可能与之有关,并找到了一篇提到了NVIDIA的某位员工---Slashdot的文章。经过一番询问,我们得知 3dfx 的创始人之一Gary Tarolli是最有可能知道这段代码来源的人。

        因此,我又向 Gary 发了一封邮件,询问他是否是本代码的作者?

上述回信的翻译: 

         

一段过去的记忆!(时间来了2005年的九月)

我确实认得下面的代码,但这不是我的功劳。

我记得大约十年前碰巧看到过它,我当时还重新推导过它。这是牛顿-拉夫逊迭代法,加上个非常巧妙的近似初值。

        我记得我当时尝试过除了0x5f3759df的其他值。我可能是在IRIS Indigo做了与之相关的工作,或者是为Kubota咨询时而做的,我现在不太确定了。

        鉴于它所执行的数学运算量、准确度以及不需要查表的特性,这真是段非常出色的代码。

        我特别喜欢“i = 0x5f3759df - (i >> 1);”这一整数运算。实际上它是在用整数做浮点运算——我当时花了很长时间才弄明白它究竟是如何计算的,但现在我已经记不清细节了。

啊,那些日子——快的整数运算,慢的浮点运算……

        这样看来,我确实在很多年前在我的键盘上敲过这段代码,我还稍微调整过那个十六进制常数,但除了我经常使用它,并且可能因此促成了它的广为流行和大量使用。我对这段代码再没有什么其他的贡献了。

附言:抱歉花了这么久才回复。

        从Gary的回复中我们得知,他确实认得这段代码,但他并不认为这是他的原创。他回忆起大约 10 年前曾经遇到过这段代码,并重新推导过它。他推测这段代码是使用牛顿迭代法和一个非常巧妙的初始近似值。虽然他曾经在调试这段代码的十六进制常量,但他并不记得详细的内容了。但他确实是为该代码的广为流行做出贡献的人之一。

        虽然,我们到目前为止都还没有找到代码的原作者,但经过 John Carmack、Michael Abrash、Terje Mathison 和 Gary Tarolli 的多方反馈,我们可能已经尽可能接近了真相。这段代码是 3D 图形编程领域的一个经典案例,它展示了软件在 3D 性能分析中的重要性。

        最后,我想感谢 John Carmack、Terje Mathison、Gary Tarolli以及其他为这段代码的传播和优化做出贡献的人。

        至此,关于代码原作者的讨论也告以段落了,且上面的讨论也被一个名叫Slashdot的报导了。巧的是,正是由于他的报导,这段代码的真正作者看到了报导,并主动联系了相关人员,这才有了下面的故事。


真正的作者找到了 ---  是Greg Walsh!

        我们本以为这就是故事的结局,于是发表了文章并宣传出去。Slashdot报道了此事,给文章带来了相当大的曝光度。正是这种曝光度让真正的作者站了出来并承认了这段代码。原来,这段代码并非出自Gary之手,而是另一位名字以G开头的硅谷老将——Greg Walsh。

        Greg自上世纪70年代以来一直是计算机行业的老兵,他的第一批大项目包括在“互联网”诞生前研究互联网和分布式计算技术,并在斯坦福大学工作期间,协助Xerox PARC开发了第一个WYSIWYG文字处理器。之后,Greg参与创立了Ardent Computer,关于该公司的更多信息,你可以在它的维基百科词条中找到。

        Ardent专门开发使用定制矢量处理器和Intel i860 RISC矢量处理器的并行图形小型计算机(Intel i860曾用于老NeXT机器中的图形子系统)。Ardent后来在Kubota(当时Ardent的资助方)的强迫下与其最大竞争对手Stellar合并,形成了Stardent。

        

        Ardent的Titan图形小型计算机当时在实现其性能目标时遇到了困难,而Greg最初是为了加速无法利用矢量硬件的软件而设计了快速1/sqrt(x)函数。当时,Greg与MathWorks的创始人兼MatLab作者Cleve Moler一起在Ardent工作。正是与Cleve的合作让Greg萌生了编写该函数的想法。Cleve当时正在研究使用牛顿-拉夫森迭代法进行近似计算,而Greg也在同一时期为Titan编写了类似的1/cuberoot(x)函数,不过方法更加复杂。

        与此同时,Gary Tarolli也在为Kubota提供咨询服务,因此我们终于明白了代码如何进入公开源码。因为Gary Tarolli也在使用并调整该代码以适应自己的需求,而他在3dfx的工作至少可以通过Brian Hook与id Software建立联系。Gary和Greg后来在Accel Graphics公司再次合作(Accel最终被Evans and Sutherland收购,另一个涉及3D领域的公司,关于它的故事你也可以写一本书)。如果有谁有多余的AccelSTAR硬件,欢迎联系我!

        因此,最终我们认定Greg是这些年来参与编写或修改这段代码的人之一。我们可以理直气壮地称这段代码灵感来自于Cleve,Greg为作者,现在大概可以为这段故事画上句号了。

        Greg后来离开3D行业,创办了一家商业软件公司,成功上市并在1999年引起轰动;Kubota最终为DEC工作站生产了插入式3D板(Denali现在大多是罕见的收藏品),然后彻底退出了3D行业;而Cleve仍然在MathWorks担任首席科学家兼创始人。

        非常感谢Greg与我们联系并提供了这些信息,也感谢Slashdot(以及提交该文章的Geo),因为没有那次大曝光,Greg可能永远不会看到原文。


        上述故事中的文字,都是我用chatGPT从外国论坛中翻译的,我只是对译文做了一丁点修改和排版。


正文

         他(Greg Walsh)的这套代码,我学习下来。其核心思想就是牛顿拉夫逊法,而众所周知,牛顿拉夫逊法的核心是通过多次迭代提升计算精度/缩小误差,最终得到我们想要的答案。而Greg Walsh这段代码的精妙之处在于出色的选择牛顿拉夫逊法的初值(对牛顿拉夫逊法而言,初值选择的越是接近真实答案就越理想,迭代后的准确率就越高),使得这段代码只需迭代一次就能接近使用其他初值多次迭代后的精度。

        为了达到这一目的,Greg Walsh在计算/选择初值的时候使用了代码中的那个神秘的magic number“5F3759DF”。而带有magic number的那句计算初值的代码,又巧妙地利用了浮点数基于IEEE 754标准在计算中的默认存储方式/形式

        现在,我把上面两段我所要表达的重点重新梳理一下,并换一种方式来陈述。Greg Walsh的这套计算平方根倒数的快速算法,实际上还包含了一个“内嵌的一个平方根倒数的快速算法”。“内嵌的平方根倒数快速算法”专门用于计算1/\sqrt{x}的初值(一个比较精确的近似值)。由于下面几句代码实现:

        有了前面的初值后,后面只用了一次牛顿拉夫逊法就完成了全部的计算,即,下面的这一句代码:


下面是对代码原理的详细剖析:

1,问题的转化

给定一个任意数x,要计算1/\sqrt{x},假设1/\sqrt{x}的值为a,则:

1/\sqrt{x}=a(式1)

\Rightarrow 1/a^{2}-x=0 

如果定义一个自变量为a的函数f(a):

f(a)= 1/a^{2}-x(式2)

则,令函数f(a)等于0的a就是我们要求的 1/\sqrt{x}

f(a)= 0\Rightarrow 1/a^{2}-x=0

        如此一来,我们最开始的问题就转化为要找到能够令函数f(a)等于0的a,即:a=?\Rightarrow f(a)=0。如果把函数f(a)画出来,则函数f(a)与a轴的交点(a,0)就是我们要找的根。

        函数的根指的是使函数的值为零的自变量,在函数的图像中,一个函数的根即这个函数与横坐标的交点。

        以x=1为例:

                1/\sqrt{1}=1得到a=1,画出函数f(a)= 1/a^{2}-1的图像可见f(a)与横坐标轴a的交点正好是(a=1,y=0)。

python code:

import matplotlib.pyplot as plt
import numpy as np

# make data
x=1
a = np.linspace(0.01,8, 1000)
y = 1/(a**2)-x

# plot
fig, ax = plt.subplots()
ax.plot(a, y, linewidth=2.0,label='f(a)')
ax.set(xlim=(-1, 8),ylim=(-2, 6))
ax.legend()
ax.grid(True)

2,用牛顿拉夫逊法(NR-iteration)求x=1时的1/\sqrt{1}

        在上面的例子中,由于x取得比较特殊,我们直接求出了1/\sqrt{1},即,令函数f(a)为0的根。现在我们依然以x=1为例,依然是求1/\sqrt{1},依然是通过求解f(a)的根。只不过是用NR-iteration去求f(a)=0的近似解。

令x=1,代入(式2)有:

f(a)= 1/a^{2}-x=1/a^{2}-1,(式3)

(式3)求导,得到:

f'(a)= -2/a^{3},(式4)


初次迭代

step1:   随机给定一个初始值a0,并求出相应的f(a0)

        用牛顿拉夫逊法需要逐级逼近,虽然我们已经知道最终的答案是a=1,但我这里假设我并不知道。所以,我随便取了一个真实答案左边的值a0=0.5(注意:这里不能取1左边的值,否则牛顿拉夫逊法会失效),代入(式3)有:

a_{0}=0.5\Rightarrow f(a_{0})=1/{0.5}^{2}-1=3.0

        得到点(0.5,3.0),这里的3.0表示实际答案与真实答案之间的误差err

step2: 把a0=0.5代入(式4)计算出该点处的斜率,并利用点斜式求出函数f(a)在点(0.5,3.0)处的切线方程(称切线为line0)

 s= -2/a_{0}^{3}= -2/0.5^{3}=-16

line0: y-3.0=s*(a-0.5)\Rightarrow y-3.0=-16*(a-0.5)

step3: 令y=0,找到切线line0与a轴的交点得到新的a,并称其为a1

0-3.0=-16*(a-0.5)\Rightarrow a=0.6875

python code:
#guess一个初值x0,并求出相应的f(a0)
a0=0.5
fa0=1/(a0**2)-x
print(f"a={a0}, Err:f(a)={fa0}")

#利用点斜式求出点(a0,f(a0))处的切线line0
#y-f(a0)=s*(a-a0)
aa=np.linspace(-1, 8, 1000)
s=-2*(a0**(-3))
print(f"slope={s}")
y0=s*(aa-a0)+fa0

#令y=0,找到切线line0与a轴的交点得到新的a1
a1=a0-fa0/s
print(f"a_new={a1}")

# plot
fig, ax = plt.subplots()
ax.plot(a, fa, linewidth=2.0,label='f(a)')
ax.plot(aa, y0,'--',label='line0')
ax.set(xlim=(-1, 8),ylim=(-2, 6))
ax.legend()
ax.grid(True)
plt.show()

程序的运行结果:


第二次迭代

        基于新的a,即上面的a1,在函数图像上找到新的点(a1,f(a1))。画该点处的切线line1,得到line1与a轴的交点又会得到一个新的a,最终完成第二次迭代。具体的计算过程我这里不再赘述,在我python代码的注释中有详细的说明。

python code:
#把a1代入函数f(a)求出相应的f(a1)
fa1=1/(a1**2)-x
print(f"a={a1}, Err:f(a)={fa1}")

#利用点斜式求出点(a1,f(a1))处的切线line1
#y-f(a1)=s*(a-a1)
s=-2*(a1**(-3))
y1=s*(aa-a1)+fa1
print(f"slope={s}")

#令y=0,找到切线line1与a轴的交点得到新的a2
a2=a1-fa1/s
print(f"a_new={a2}")

# plot
fig, ax = plt.subplots()
ax.plot(a, fa, linewidth=2.0,label='f(a)')
ax.plot(aa, y0,'--',label='line0')
ax.plot(aa, y1,'--',label='line1')
ax.set(xlim=(-1, 8),ylim=(-2, 6))
ax.legend()
ax.grid(True)
plt.show()
程序的运行结果:


第三次迭代

        后续的迭代无非就是个重复上面的过程,求点,画切线和求交点更新a,直到误差err的值足够小,到那个时候,我们就声称已经求得最终的a了,即,1/\sqrt{1}的值(准备的说是达到一定精度的近似值)。

python code:
#把a2代入函数f(a)求出相应的f(a2)
fa2=1/(a2**2)-x
print(f"a={a2}, Err:f(a)={fa2}")

#利用点斜式求出点(a2,f(a2))处的切线line2
#y-f(a2)=s*(a-a2)
s=-2*(a2**(-3))
y2=s*(aa-a2)+fa2
print(f"slope={s}")

#令y=0,找到切线line2与a轴的交点得到新的a3
a3=a2-fa2/s
print(f"a_new={a3}")

# plot
fig, ax = plt.subplots()
ax.plot(a, fa, linewidth=2.0,label='f(a)')
ax.plot(aa, y0,'--',label='line0')
ax.plot(aa, y1,'--',label='line1')
ax.plot(aa, y2,'--',label='line2')
ax.set(xlim=(-1, 8),ylim=(-2, 6))
ax.legend()
ax.grid(True)
plt.show()
程序的运行结果:


第四次迭代

python code:
#把a3代入函数f(a)求出相应的f(a3)
fa3=1/(a3**2)-x
print(f"a={a3}, Err:f(a)={fa3}")

#利用点斜式求出点(a3,f(a3))处的切线line3
#y-f(a3)=s*(a-a3)
s=-2*(a3**(-3))
y3=s*(aa-a3)+fa3
print(f"slope={s}")

#令y=0,找到切线line3与a轴的交点得到新的a4
a4=a3-fa3/s
print(f"a_new={a4}")

# plot
fig, ax = plt.subplots()
ax.plot(a, fa, linewidth=2.0,label='f(a)')
ax.plot(aa, y0,'--',label='line0')
ax.plot(aa, y1,'--',label='line1')
ax.plot(aa, y2,'--',label='line2')
ax.plot(aa, y3,'--',label='line3')
ax.set(xlim=(-1, 8),ylim=(-2, 6))
ax.legend()
ax.grid(True)
plt.show()
程序的运行结果: 


第五次迭代

python code:
#把a4代入函数f(a)求出相应的f(a4)
fa4=1/(a4**2)-x
print(f"a={a4}, Err:f(a)={fa4}")

#利用点斜式求出点(a4,f(a4))处的切线line4
#y-f(a4)=s*(a-a4)
s=-2*(a4**(-3))
y4=s*(aa-a4)+fa4
print(f"slope={s}")

#令y=0,找到切线line4与a轴的交点得到新的a5
a5=a4-fa4/s
print(f"a_new={a5}")

# plot
fig, ax = plt.subplots()
ax.plot(a, fa, linewidth=2.0,label='f(a)')
ax.plot(aa, y0,'--',label='line0')
ax.plot(aa, y1,'--',label='line1')
ax.plot(aa, y2,'--',label='line2')
ax.plot(aa, y3,'--',label='line3')
ax.plot(aa, y4,'--',label='line4')
ax.set(xlim=(-1, 8),ylim=(-2, 6))
ax.legend()
ax.grid(True)
plt.show()
程序的运行结果: 


3,NR-iteration的计算公式 

        下面这张表格记录了上面每一步的结果,可以看到牛顿拉夫逊法的收敛速度还是比较快的。经过5次迭代基本上已经非常接近准确答案1了。 

牛顿拉夫逊迭代法的更新过程
iteration numaa_newErr
10.50.6873
20.6870.8681.12
30.8680.9750.32
40.9750.99909230.0513
50.99909230.99999880.0018

        在完全熟悉了牛顿拉夫逊法以后,上面的5次迭代过程可以通过下面的计算通式(也叫迭代公式)来表示(可自行推导,下图中的公式来自维基百科),从而免去了前面繁琐的计算过程。其中,x_{n}表示前一次的计算结果,x_{n+1}表示本次迭代更新后的x。

x_{n+1}=x_{n}-\frac{f(x_{n})}{f'(x_{n})}(迭代方程)

        应用在本例中,上式需改写成:

a_{n+1}=a_{n}-f(a_{n})/f'(a_{n}) 

 其中:

f(a_{n})= 1/{a_{n}}^{2}-1(式3)

 f'(a_{n})= -2/{a_{n}}^{3}(式4)

代入迭代方程,得到:

 a_{n+1}=a_{n}-f(a_{n})/f'(a_{n})

\Rightarrow a_{n+1}=a_{n}(1.5-x{a_{n}}^{2}/2)(式5)

        现在我们来试一下这个公式,我们将会看到他的计算结果和我们之前的计算结果如出一辙。令a的初值a_{n}=0.5,代入(式5)得到新的a。然后不断的把新得到的a代入(式5)迭代,得到如下结果:


4,NR-iteration的初值 

         上面的迭代过程说明了一个问题。如果我们的初值选择的足够好,我们就能很快完成迭代。这里我们以x=2为例,即求1/\sqrt{2}。因为我们都知道根号2的值大约是1.414,所以根号2的倒数也就是约等于1/1.414约等于0.707。为了加速NR-iteration的迭代过程,尽可能快的得到精确值,我们直接令初值a=0.707并代入迭代公式,得到:

第一次迭代后的估计值:

这是用python计算器算出来的精确值: 

        相当于只迭代了一次就已经精确到小数点后第7位了!!!而如果仍然是选择0.5作为初值的话,需迭代5次才能超过上面迭代一次后的精度。

        而这就是平方根倒数快速算法的秘密:尽可能精确的选择NR-iteration的初值。现在我们再回到前面推导的结果(式5)

a_{n+1}=a_{n}(1.5-x{a_{n}}^{2}/2)(式5)

如果我们令a_{n+1},a_{n}为y,再令x/2为x2,则(式5)变为:

 y=y*(1.5-x2*y^{2})=y*(1.5-x2*y*y)

        这个变形后的公式不就是平方根倒数快速算法code中的最后一行吗?!

        这说明了以下几点:

        1,这段代码的作者Greg Walsh用到了牛顿拉夫逊法NR-iteration(毕竟code中的代码和推导出的公式一样)

        2,这段代码中所使用的初值y,必然是一个非常精确的初始值(否则,不可能只需迭代了一次)。

        3,这个非常精确的初始值是由批注为“what the fuck?”那一行求出来的。

        4,也许正是因为初始值选的好,这段代码中的最后一行2nd NR-iteration被注释掉了。也就是说整个函数只需迭代了一次就足以达到了一个较高的精度。

数值计算 --- 平方根倒数快速算法(中)-CSDN博客文章浏览阅读330次,点赞10次,收藏18次。由于平方根倒数快速算法实在是太过经典,出于对code中magic number"0x5f3759df"的好奇,出于对John Carmack“what the fuck!”这一锐评的热爱,还有出于对于这一算法的原作者Greg Walsh的致敬。我写了这篇文章,作为该算法的自学笔记。https://blog.csdn.net/daduzimama/article/details/142444260


(全文完) 

--- 作者,松下J27

参考文献(鸣谢):

1,https://en.wikipedia.org/wiki/Newton%27s_method#Examples

2,什么代码让程序员之神感叹“卧槽”?改变游戏行业的平方根倒数算法_哔哩哔哩_bilibili

3,[算法] 平方根倒数速算法中的魔数0x5f3759df的来源 | GeT Left

4,https://i.hsfzxjy.site/uncover-the-secret-of-fast-inverse-square-root-algorithm/

5,https://www.youtube.com/watch?v=p8u_k2LIZyo

6,计算机中的浮点数(一)_浮点表示法-CSDN博客

7,计算机中的浮点数(二)-CSDN博客 

8,Beyond3D - Origin of Quake3's Fast InvSqrt()

9,Beyond3D - Origin of Quake3's Fast InvSqrt() - Part Two

10,The Fast Inverse Square Root

11,How Fast Inverse Square Root actually works

版权声明:所有的笔记,可能来自很多不同的网站和说明,在此没法一一列出,如有侵权,请告知,立即删除。欢迎大家转载,但是,如果有人引用或者COPY我的文章,必须在你的文章中注明你所使用的图片或者文字来自于我的文章,否则,侵权必究。 ----松下J27  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2168134.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何投IEEE论文(Transactions on Cybernetics为例)

文章目录 0. 建议先看看投稿要求1.下载对应的论文模板2.进入提交论文信息的界面3.填写论文中必要的信息3.1 Article Type3.2 Upload Manuscript3.3 Title3.4 Abstract3.5 Authors3.6 Author Details3.7 Math Organizations3.8 Additional Information3.9 Final Review 终审 0. …

计算1 / 1 - 1 / 2 + 1 / 3 - 1 / 4 + 1 / 5 …… + 1 / 99 - 1 / 100 的值,打印出结果

我们写这道题的时候需要俩变量接受&#xff0c;一个总数一个分母&#xff0c;我们发现分母变化是有规律的从1~100循环。 #include<stdio.h> int main() {int i 0;int tag 1;double sum 0.0;for (i 1; i < 101; i){if (i % 2 0){sum sum - 1.0 / i;}else{sum s…

实力!云起无垠荣获“创业邦2024年AIGC创新企业及产品创新”双奖

近日&#xff0c;国际创新生态服务平台创业邦郑重发布 “2024 年 AIGC 创新企业及产品创新榜单”。云起无垠凭借卓越的企业能力与突出的产品创新&#xff0c;成功入选 “2024 年 AIGC 创新企业 100 强” 以及 “2024 年 AIGC 产品创新 100 强”。 本次评选采用内部初审与外部专…

深入分析MySQL事务日志-Undo Log日志

文章目录 InnoDB事务日志-Undo Log日志2.1 Undo Log2.1.1 Undo Log与原子性2.1.2 Undo的存储格式1&#xff09;insert类型Undo Log2&#xff09;delete类型Undo Log3&#xff09;update类型Undo Log 2.1.3 Undo Log的工作原理2.1.4 Undo Log的系统参数2.1.5 Undo Log与Purge线程…

【Linux 报错】vim 保存文件时出现 E45: ‘readonly‘ option is set (add ! to override)

一、错误原因 该错误表明当前你尝试保存的是一个 只读文件&#xff0c;该文件权限设置为只读&#xff0c;具有只读的标识 系统为了防止你意外修改该只读文件&#xff0c;因此会阻止对只读文件的保存&#xff08;他怕你修改了你还保存&#xff0c;破坏了只读属性&#xff09; …

媒界:2025河南台球及配套设施展会3月举办

立足中原&#xff0c;辐射全国&#xff0c;壹肆柒中国国际台球产业博览会3月在郑州盛大举办&#xff1b; 2025中国&#xff08;郑州&#xff09;国际台球产业博览会&#xff08;壹肆柒台球展&#xff09; The 2025 China (Zhengzhou) International Billiards Industry Expo …

2021年的burpsuite安装。

安装burpsuite 很简单的。 1.要有java环境&#xff0c;也就是jdk&#xff0c;并且jdk版本要与burpsuite要对应。&#xff08;如果你的bp安装不起&#xff0c;可能是你的jdk版本不对&#xff09; 2.就是按照我都步骤走就行。 3.下载完文件之后&#xff0c;全程离线操作 说明一下…

spring boot 项目如何使用jasypt加密

1.首先&#xff0c;添加jasypt依赖 <dependency><groupId>com.github.ulisesbocchio</groupId><artifactId>jasypt-spring-boot-starter</artifactId><version>2.1.0</version></dependency> 2.然后winr&#xff0c;cmd调出窗…

Rapid品牌SSL证书通配符单域名申请窍门

RapidSSL最初以FreeSSL的名称引入数字世界&#xff0c;于2003年诞生&#xff0c;扎根于以技术为中心的加利福尼亚州山景城。如今&#xff0c;它是GeoTrust旗下一家值得骄傲的子公司&#xff0c;其战略定位是满足市场对经济高效的SSL证书解决方案的需求。 Rapid属于Geotrust品牌…

Redis篇(初识Redis)

目录 一、数据库 二、NoSQL 三、认识Redis 三、关系数据库与非关系数据库对比 1. 结构化与非结构化 2. 关联和非关联 3. 查询方式 4. 事务 5. 存储方式 6. 扩展性 7. 总结 7.1. 图形梳理 7.2. 表格梳理 7.3. 优缺点 关系型数据库 非关系型数据库 四、再次认识R…

LeetCode142. 环形链表 II(2024秋季每日一题 28)

给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数…

C++:STL详解(二)string类的模拟实现

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;他强任他强&#xff0c;清风拂山冈&#xff01; &#x1f4ab; 欢迎来到我的学习笔记&#xff01; &#x1f525;&#x1f525;&#x1f525;&#x1f525;&#x1f525;本文参考文章&…

合同管理中的常见陷阱,你是否也中招了?

在合同管理中&#xff0c;很多企业常常会掉进一些意想不到的“陷阱”。这些问题可能在日常工作中看似无关紧要&#xff0c;但一旦忽视&#xff0c;往往会引发一系列严重的后果。合同信息管理混乱&#xff0c;导致重要条款被遗漏&#xff0c;签署文件时出现纰漏&#xff1f;合同…

Pygame中Sprite实现逃亡游戏4

在《Pygame中Sprite实现逃亡游戏3》中实现了玩家跳跃飞火的效果&#xff0c;接下来通过精灵类的碰撞检测来判断飞火是否击中玩家、飞火是否击中飞龙以及飞龙是否抓住玩家。 1 飞火是否击中玩家的判断 判断飞火是否击中玩家的代码如图1所示。 图1 判断飞火是否击中玩家的代码 …

大数据-144 Apache Kudu 基本概述 数据模型 使用场景

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

企业私有网盘怎么搭建?整理国内外主流 18 家方案厂商

目前市场上主流的企业网盘有360亿方云、坚果云、百度企业网盘、联想企业网盘、燕麦云智能企业云盘、腾讯企业网盘等等。就创立时间和用户量来看&#xff0c;联想企业网盘、360亿方云可以说第一梯队的存在。 企业需要的不仅仅是文档存储、分享和协作的简单管理工具&#xff0c;还…

AI无人直播新标杆,一站式直播解决方案:打造专属舞台!

AI无人直播新标杆&#xff0c;一站式直播解决方案&#xff1a;打造专属舞台&#xff01; 在数字化浪潮的汹涌澎湃中&#xff0c;AI技术正以前所未有的速度渗透至各行各业&#xff0c;其中&#xff0c;直播行业作为数字内容传播的重要阵地&#xff0c;正经历着一场由AI引领的深刻…

pr视频剪辑、福昕剪辑……四款剪辑视频大比拼

最近入了视频剪辑的坑&#xff0c;我最近在尝试不同的视频剪辑软件&#xff0c;想找到最适合我的那一款。今天&#xff0c;我就来跟大家分享一下我使用福昕视频剪辑、爱拍视频剪辑、Adobe Premiere&#xff08;简称PR&#xff09;和Shotcut这四款软件时的一些体验和感受。希望我…

移动硬盘突然打不开:深度剖析、恢复策略与预防措施

突发困境&#xff1a;移动硬盘的沉默拒绝 在日常的数字生活中&#xff0c;移动硬盘作为数据存储与传输的重要工具&#xff0c;扮演着不可或缺的角色。然而&#xff0c;当您急需访问存储在移动硬盘中的重要文件时&#xff0c;却遭遇了“突然打不开”的尴尬境地&#xff0c;这无…

C++ 3 个有序点的方向(Orientation of 3 ordered points)

给定三个点 p1、p2 和 p3&#xff0c;任务是确定这三个点的方向。 平面中有序三重点的方向可以是 逆时针 顺时针 共线 下图显示了 (a,b,c) 的不同可能方向 如果 (p1, p2, p3) 的方向共线&#xff0c;则 (p3, p2, p1) 的方向也共线。 如果 (p1, p2, p3) 的方向是顺时针&a…