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

news2024/12/28 22:52:09

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

1,平方根倒数快速算法是如何选择初值的?WTF中的神秘数字究竟是怎么来的?

        花开两朵,各表一枝。在前面的介绍中,我们已经知道了这段代码的作者在函数的最后使用了NR-iteration,且只用了一次NR-iteration。这样一来,选择正确的初值就显得尤为重要了。在源代码中,求解NR-iteration所需初始值的关键在于充分的利用浮点数x在计算机中的表示/编码方式

        由于code中的x为浮点数(注意:我这里的x就是代码中的number)。则根据标准IEEE 754,x的二进制浮点数表示如下(准确的说应该叫normal number的表示):

(-1)^{S}\times 2^{E-b}\times (1+T\cdot 2^{1-p})

         又因为x不能为负(负数没法进行开根号运算),符号位S默认为0,则浮点数x在计算机中的二进制可表示如下:

x= (1+T\cdot 2^{1-p})\times 2^{E-b}

 对于单精度float而言,p=24,b=127,则:

x= (1+T\cdot 2^{-23})\times 2^{E-127}

 我们对x取以2为底的对数,得到:

{log_{2}}^{x}={log_{2}}^{(1+T\cdot 2^{-23})\times 2^{E-127}}={log_{2}}^{(1+T\cdot 2^{-23})}+{E-127}

再令:

 M=T\cdot 2^{-23}

则上式变为:

 {log_{2}}^{x}={log_{2}}^{(1+T\cdot 2^{-23})\times 2^{E-127}}

 ={log_{2}}^{(1+T\cdot 2^{-23})}+{E-127}={log_{2}}^{(1+M)}+{E-127}

 即:

{log_{2}}^{x}={log_{2}}^{(1+M)}+{E-127}(式5)

        注意,T字段所保存的是trailing significand,即,放大一定精度后的有效数字的尾数/有效数字的小数部分(默认隐含了首位1)。计算机在保存T时把小数点右移了23位,即,乘以2^{23}。因此,在读取T时才有了上面的T\cdot 2^{-23}。这就是说上面的M实际上是“1.xxxxx...”中的“0.xxxxx...”部分,是一个介于0~1之间的数。

为了更好的理解M,这里插播一个例子,1/3是如何被保存成二进制浮点数的?

        计算机使用二进制浮点数表示小数时,采用的是 IEEE 754 浮点数标准。由于1/3是一个无限循环小数,在二进制中它也不能被精确表示,所以计算机只能以有限的精度近似存储它。

1. 十进制转二进制

在十进制下,1/3​=0.33333...是一个循环小数。转换到二进制后为:

\frac{1}{3}_{10}=0.333..._{10}=0.01010101..._{2}

 

        也是一个二进制的循环小数,但由于计算机只能只能保存有限的位数,这个循环小数在保存时会被截断,得到一个近似值。

2. IEEE 754 浮点数表示

在 IEEE 754 单精度浮点数标准中,32位浮点数的表示结构如下:

  • 1 位符号位:表示正数或负数
  • 8 位指数:存储实际指数的偏移量(偏移 127)
  • 23 位尾数(有效数字):存储归一化的尾数,隐含首位为 1 的小数部分

对于1/3计算机会将其转换为二进制表示,然后使用以下步骤:

  1. 标准化二进制小数:将二进制小数表示成规范形式。规范形式要求小数点左侧只能有一位,且必须是1,因此:0.01010101..._{2}=1.01010101..._{2}\times 2^{-2}

  2. 计算指数E:指数部分需要加上偏移量(127)。所以,计算机所保存的指数E等于上面的实际指数−2加上127。−2+127=125,再转换为二进制后为 01111101​。

  3. 有效数字的尾数T:有效数字尾数的精度共 23 位,因此我们在保存小数部分时,去掉整数部分的1不保存:1.01010101..._{2}\Rightarrow 0.01010101..._{2}

        然后再把小数点右移23位,得到:

0.01010101..._{2}\Rightarrow 01010101010101010101010_{2}

4.最终存储形式:将符号位(0,正数)、指数(125 的二进制表示 011111010111110101111101)和尾数组合起来,得到:

00111110101010101010101010101010

这就是1/3的IEEE 754单精度浮点数表示。

        由于M是一个0~1之间的小数,人们发现当M=0~1时,函数y=log2(1+M)与y=M的函数值差异很小。

Matlab code:

close all
clear all

x=0.01:0.01:pi/2;
f1=log2(1+x);
f2=x;
plot(x,f1,x,f2);
grid on;
legend("y=log2(1+M)","y=x")

diff=abs(f1-f2);
figure
plot(x,diff)
legend("diff")

        因此,我们认为在x=0~1之间:

 {log_{2}}^{(1+M)}\approx M

         基于这一近似,式5变为:

{log_{2}}^{x}={log_{2}}^{(1+M)}+E-127=M+E-127

 =T\cdot 2^{-23}+E-127=1/2^{-23}\cdot (E\times 2^{23}+T)-127

        又因为括号中的E\times 2^{23}+T,正好是浮点数x在计算机中的存储形式(我们这里用x_{B}来表示),即:

x_{B}=E\times 2^{23}+T

这里,我们再插播一下。如果还是以上面插播信息中的1/3为例的话。我文章中的x就是1/3(十进制),而x_{B}就是上面那个例子中最终保存的00111110101010101010101010101010。他们是一个数,只不过一个是实际数,一个是在计算机中存的数。

        如此一来,我们利用浮点数x在计算机中默认的二进制存储方式,得到了log2(x)的表示方式:

 {log_{2}}^{x}=1/2^{-23}\cdot (E\times 2^{23}+T)-127=1/2^{-23}\cdot x_{B}-127

 {log_{2}}^{x}=x_{B}/2^{23}-127(式6)

        现在我们再回到计算1/\sqrt{x}的近似值问题。根据(式1)我们知道:

a=1/\sqrt{x}=x^{-1/2}

 对上式两边同时取以2为底的对数,得到:

{log_{2}}^{a}={log_{2}}^{x^{-1/2}}

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

根据前面推导出的log2(x)的表示方式(式6)

 {log_{2}}^{a}=a_{B}/2^{23}-127-1/2\cdot {log_{2}}^{x}=-1/2\cdot (x_{B}/2^{23}-127)

 a_{B}/2^{23}-127=-1/2\cdot (x_{B}/2^{23}-127)

 a_{B}=381\times 2^{22}-x_{B}/2

其中381\times 2^{22}=1598029824这个数,如果用十六进制来表示的话就是:

 381\times 2^{22}=5f400000

则上式变为:

 a_{B}=5f400000-x_{B}/2(式7)

        这个十六进制的数code中的那个神秘数字“5f3759df”已经比较接近了,而这个数表示成十进制是1597463007。 

        这里我们暂时先不讨论这两个十六进制常数的差异,先看看(式7)究竟表示什么意思:

a_{B}=5f400000-x_{B}/2(式7)

        我们知道a就是我们要求的十进制数x的平方根的倒数,而我们又知道不论十进制数a或x是多少,他在计算机中都要以二进制浮点数的方式被保存为a_{B}x_{B}的形式。因此,(式7)的意思是说,对于一个已经按照IEEE 754标准被保存好的十进制浮点数x,他在计算机中换了个样子,变成了x_{B},但他仍然等于x。而要想求得x_{B}的平方根的倒数,只需按照(式7)就能快速求出近似值a_{B},这个a_{B}是与之对应的十进制浮点数a,保存在计算机中的样子。而要想把a_{B}再变成a,只需按照浮点数的编码方式解析出来即可。

        现在让我们再回到原代码,我们注意到评论为WTF的上下两句所做的正是我在上文中所描述的过程。所不同的是代码中的y是我文中的x,代码中的i是我文中的x_{B},代码中的经过神秘数字“5f3759df”计算后的新i是我文中的a_{B},而把新i重新解码后的浮点数y是我文中的a:

        现在,我们有了能够快速求解出较为精确的1/\sqrt{x}的公式(式7),再加上之前根据牛顿拉夫逊法求得的(式4)a_{n+1}=a_{n}(1.5-x{a_{n}}^{2}/2)。至此,我们基本上复现了平方根倒数快速算法的全部过程,且和原始code一致(除了magic number之外)。

       我们来试试我们现有的快速算法,看看他的效果究竟怎么样,还是以x=1为例,求1/\sqrt{1}

C code:

# include <stdio.h>
# include <math.h>

float Q_rsqrt(float number)
{
	long i;
	float x2, y;
	const float threehalfs = 1.5F;

	x2 = number * 0.5F;
	y = number;
	i = *(long*)&y;                       // evil floating point bit level hacking
	i = 0x5f3759df - (i >> 1);               // what the fuck?
	y = *(float*)&i;
	y = y * (threehalfs - (x2 * y * y));   // 1st iteration
	// y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

	return y;
}

float myQ_rsqrt(float number)
{
	long i;
	float x2, y;
	const float threehalfs = 1.5F;

	x2 = number * 0.5F;
	y = number;
	i = *(long*)&y;                       // evil floating point bit level hacking
	i = 0x5f400000 - (i >> 1);
	y = *(float*)&i;
	y = y * (threehalfs - (x2 * y * y));   // 1st iteration
	// y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

	return y;
}

int main() {
	float x = 4.0f;
	float y = 0,yy=0;
	y=Q_rsqrt(x);
	yy = myQ_rsqrt(x);

	printf("input x=%f\n", x);
	printf("ideal result=%f\n", 1/sqrt(x));
	printf("calc with 5f3759df=%f\n", y);
	printf("calc with 5f400000=%f\n", yy);

    return 0;
}

 相应的输出为:

        就本例而言,二者的计算结果都非常接近准确值1,但5f400000的精度要更高,5f3759df的误差约为0.002。 如果以x=4为例,准确值为0.5,再看看二者的表现:

        结果还是5f400000的准确性更高,5f3759df的误差约为0.0009。但上面的两个例子都是平方根的结果正好是整数的情况,例如\sqrt{1}=1\sqrt{4}=2。但如果碰到平方根为无理数的情况呢,我们分别试试x=2和x=3的情况。

        有趣的是,在这两个例子中基于magic number的计算结果要比5f400000的精度高。对于x=2而言,5f400000的误差约为0.004,5f3759df的误差约为0.0002。 对于x=3而言,5f400000的误差约为0.006,5f3759df的误差约为0.0005。 

        这样看来,不论是采取哪种常数去估算初值,基本上都已经能够得到较为准确的结果。毕竟,这一初值还要用牛顿拉夫逊法再迭代一次才是最终的结果。


(全文完) 

--- 作者,松下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

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

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

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

相关文章

CVE-2024-46103

前言 CVE-2024-46103 SEMCMS的sql漏洞。 漏洞简介 SEMCMS v4.8中&#xff0c;SEMCMS_Images.php的search参数&#xff0c;以及SEMCMS_Products.php的search参数&#xff0c;存在sql注入漏洞。 &#xff08;这个之前就有两个sql的cve&#xff0c;这次属于是捡漏了&#x1f6…

【MATLAB源码-第268期】基于simulink的永磁同步电机PMSM双闭环矢量控制系统SVPWM仿真,输出转速响应曲线。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 永磁同步电机&#xff08;PMSM&#xff09;是目前工业领域中广泛使用的一种高效电机&#xff0c;其具有高功率密度、运行效率高、动态响应快等优点。在控制永磁同步电机时&#xff0c;通常采用矢量控制&#xff08;也称为磁场…

新160个crackme - 060-snake

运行分析 需破解Name和Serial PE分析 32位&#xff0c;未知程序和壳 点击Scan/t按钮外部扫描&#xff0c;发现是C程序 静态分析&动态调试 ida搜索关键字符串&#xff0c;双击进入 发现无法反编译 选中该函数&#xff08;地址&#xff1a;401048 - 401172&#xff09;Edit -…

认识结构体

目录 一.结构体类型的声明 1.结构的声明 2.定义结构体变量 3.结构体变量初始化 4.结构体的特殊声明 二.结构体对齐(重点难点) 1.结构体对齐规则 2.结构体对齐练习 (一)简单结构体对齐 (二)嵌套结构体对齐 3.为什么存在内存对齐 4.修改默认对齐数 三.结构体传参 1…

PMP--二模--解题--51-60

文章目录 14.敏捷--术语表--完成的定义DoD--它是团队需要满足的所有标准的核对单&#xff0c;只有可交付成果满足该核对单才能视为准备就绪可供客户使用。51、 [单选] 在冲刺计划会议上&#xff0c;Scrum主管重申&#xff0c;如果在冲刺结束时敏捷项目团队正在构建的产品增量没…

五种IO模型和阻塞IO

文章目录 五种 IO 模型和阻塞 IO1、五种 IO 模型1.1、阻塞 IO1.2、非阻塞 IO1.3、信号驱动 IO1.4、IO 多路转接1.5、异步 IO1.6、总结 2、高级 IO 概念2.1、同步通信&#xff08;synchronous communication&#xff09;和异步通信&#xff08;asynchronous communication&#…

第十五章:使用html、css、js编程制作一个网页版的下雪场景动画

背景:这是一个充满诗意的下雪场景代码。打开网页时,雪花轻轻飘落,覆盖住你的屏幕,仿佛置身于冬日的夜空下。背景音乐《我期待的不是雪》缓缓响起,伴随着雪花的飘动,仿佛心中的那份爱与温柔悄然绽放。 雪花的飘落是梦境般的存在,每一片雪花都是轻盈的告白,旋转着从天际…

使用GitHub Actions自动发布electron多端安装程序

GitHub Actions 是一个强大的自动化工具&#xff0c;可以帮助开发者在 GitHub 仓库中自动化构建、测试和部署工作流程。我们的客户端就是使用github action来打包项目发布的。 以下是关于 GitHub Actions 自动化构建的一些关键点和步骤&#xff1a; GitHub Actions 的基本概念…

go注册中心Eureka,注册到线上和线下,都可以访问

go注册中心Eureka&#xff0c;注册到线上和线下&#xff0c;都可以访问 本地通过127访问&#xff0c; 线上通过内网ip访问 package mainimport ("github.com/SimonWang00/goeureka""github.com/gin-gonic/gin""wbGo/controller""wbGo/task…

【工具变量】地市环保法庭试点城市DID数据集(2005-2023年)

数据简介&#xff1a;环保法庭是中国司法体系中专门处理环境资源案件的审判机构&#xff0c;其主要职责包括审理涉及自然环境污染、矿产资源保护、自然资源环境开发等环境资源民事纠纷案件&#xff0c;对不服下级人民法院生效裁判的环境资源民事案件进行审查&#xff0c;以及对…

Java_Se--方法

方法就是一个代码片段. 类似于 C 语言中的 "函数"。方法存在的意义(不要背, 重在体会): 1. 是能够模块化的组织代码 ( 当代码规模比较复杂的时候 ). 2. 做到代码被重复使用 , 一份代码可以在多个位置使用 . 3. 让代码更好理解更简单 . 4. 直接调用现有方法开…

cv中每个patch的关联

在计算机视觉任务中&#xff0c;当图像被划分为多个小块&#xff08;patches&#xff09;时&#xff0c;每个 patch 的关联性可以通过不同的方法来计算。具体取决于使用的模型和任务&#xff0c;以下是一些常见的计算 patch 关联性的方法&#xff1a; 1. Vision Transformer (…

IDA Pro-代码结构识别

Lab06-01.exe分析 1.由main 函数调用的唯一子过程中发现的主要代码结构是什么? if语句结构 找到main函数中唯一调用的函数&#xff0c;并进入 判断网络是否链接成功&#xff0c;如果返回0走右边未连接成功 2.位于0x40105F的子过程是什么? 将字符串压栈&#xff0c;猜测…

双非本 985 硕士,秋招上岸字节算法岗!

最近已有不少大厂都在秋招宣讲了&#xff0c;也有一些在 Offer 发放阶段。 节前&#xff0c;我们邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对新人如何快速入门算法岗、如何准备面试攻略、面试常考点、大模型项目落地经验分享等热门话题进行了深入的讨论。…

面向对象程序设计——set容器の简析

1.set的介绍 • 序列式容器和关联式容器 • 我们已经接触过STL中的部分容器如&#xff1a;string、vector、list、deque、array、forward_list等&#xff0c;这些容器统称为序列式容器&#xff0c;因为逻辑结构为线性序列的数据结构&#xff0c;两个位置存储的值之间⼀般没有紧…

Python GUI 编程:tkinter 初学者入门指南——窗口

目录&#xff1a; 创建窗口更改窗口标题更改窗口大小和位置窗口在屏幕上居中窗口设置的其他属性 Tkinter 是在 Python 中开发 GUI&#xff08;图形用户界面&#xff09;最常用的库。在本指南中&#xff0c;我们将引导您了解 Tkinter 的基本知识&#xff0c;学习如何使用 Tkinte…

汽车电子零部件(16):ZCU区域控制器

ZCU(Zone Control Unit,区域控制器),功能主要包括哦数据交互、信号控制及电力分配等,是智能网联汽车中不可或缺的关键组件,ECU负责车身、车门、车窗、天窗、车灯(外大灯、内氛围灯)、座椅(可能包括座椅音响)、雷达甚至后排娱乐系统等控制执行单元的集中化。 CCU(centr…

【后端开发】JavaEE初阶—Theard类及常见方法—线程的操作(超详解)

前言&#xff1a; &#x1f31f;&#x1f31f;本期讲解多线程的知识哟~~~&#xff0c;希望能帮到屏幕前的你。 &#x1f308;上期博客在这里&#xff1a;【后端开发】JavaEE初阶—线程的理解和编程实现-CSDN博客 &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondl…

Vue3新组件transition(动画过渡)

transition组件&#xff1a;控制V-if与V-show的显示与隐藏动画 1.基本使用 <template><div><button click"falg !falg">切换</button><transition name"fade" :enter-to-class"etc"><div v-if"falg&quo…

【开源服务框架】Dubbo

&#x1f384;欢迎来到边境矢梦的csdn博文&#x1f384; &#x1f384;本文主要梳理Java面试中开源服务框架Dubbo会涉及到的知识点 &#x1f384; &#x1f308;我是边境矢梦&#xff0c;一个正在为秋招和算法竞赛做准备的学生&#x1f308; &#x1f386;喜欢的朋友可以关注一…