【学习笔记】使用魔数快速求立方根

简介

介绍使用魔数0x2a517d47快速求立方根 x 3 {\sqrt[3]{x}} 3x ​的C语言实现和公式的推导。

代码

float MagicCubeRoot(float x)
{float xthird = 0.333f * x;int i = *(int*)&x;i = (0x2a517d47 + (0.333f * i));x = *(float*)&i;x = 0.667f * x + xthird / (x * x);return x;
}

代码用于快速计算立方根 x 3 {\sqrt[3]{x}} 3x ​。

代码中核心部分是

i = (0x2a517d47 + (0.333f * i));

该行代码就完成了计算立方根。

另外再使用一次牛顿迭代法提高下精度

x = 0.667f * x + xthird / (x * x);

所以整个计算过程就是

1.i = *(int*)&x;

将输入的数转换成整数

2.i = (0x2a517d47 + (0.333f * i));

通过魔数完成立方根的计算。

3.x = *(float*)&i;

转换回浮点数。

4.x = 0.667f * x + xthird / (x * x);

使用一次牛顿迭代法提高下精度。

完成快速计算立方根 x 3 {\sqrt[3]{x}} 3x ​。

如果需要提高精度,可以多进行一次牛顿迭代。

float MagicCubeRoot(float x)
{float xthird = 0.333f * x;int i = *(int*)&x;i = (0x2a517d47 + (0.333f * i));x = *(float*)&i;x = 0.667f * x + xthird / (x * x);x = 0.667f * x + xthird / (x * x);return x;
}

注意,上面的函数只支持正数,传进来的值x需要大于或等于0。

如果要支持负数,可以增加如下判断处理。

float MagicCubeRoot(float x)
{if (x < 0){x = -x;float xthird = 0.333f * x;int i = *(int*)&x;i = (0x2a517d47 + (0.333f * i));//i = (int) (0x2a517d3c + (0.333f * i));x = *(float*)&i;x = 0.667f * x + xthird / (x * x);//x = 0.667f * x + xthird / (x * x);x = -x;}else{float xthird = 0.333f * x;int i = *(int*)&x;i = (0x2a517d47 + (0.333f * i));//i = (int) (0x2a517d3c + (0.333f * i));x = *(float*)&i;x = 0.667f * x + xthird / (x * x);//x = 0.667f * x + xthird / (x * x);}return x;
}

牛顿迭代法计算方式

一般计算立方根可以使用牛顿迭代法。

x n + 1 = x n − f ( x n ) f ′ ( x n ) x_{n+1} = x_n - \frac{f(x_n)}{f'(x_n)} xn+1​=xn​−f′(xn​)f(xn​)​

我们计算立方根的公式:

y = x 3 = x 1 3 y = {\sqrt[3]{x}} = x^{\frac 13} y=3x ​=x31​

所以

y 3 = x {{y}^3} = x y3=x

构建以y为自变量的函数方程为

f ( y ) = y 3 − x = 0 f(y) = {{y}^3} - x = 0 f(y)=y3−x=0

f ′ ( y ) = 3 y 2 = 0 f'(y) = {3}{y^2} = 0 f′(y)=3y2=0

将 f ( y ) f(y) f(y)和 f ′ ( y ) f'(y) f′(y)带入

y − f ( y ) f ′ ( y ) = y − y 3 − x 3 y 2 y - \frac{f(y)}{f'(y)} = y - \frac{{{y}^{3}} - x}{{3}{y^2}} y−f′(y)f(y)​=y−3y2y3−x​

​ = 1 3 ( 2 y + x y 2 ) = \frac{1}{3}{(2y + \frac{x}{y^2})} =31​(2y+y2x​)

所以

y n + 1 = 1 3 ( 2 y n + x y n 2 ) y_{n+1} = \frac{1}{3}{(2y_n + \frac{x}{y_n^2})} yn+1​=31​(2yn​+yn2​x​) = ( 2 y n + x ÷ y n 2 ) ÷ 3 (2{y_n} + {x}÷{y_n^2})÷3 (2yn​+x÷yn2​)÷3

实际计算,设 x = 2 x = 2 x=2, y 1 = 2 y_1 = 2 y1​=2

y 2 = ( 2 × 2 + 2 ÷ 2 2 ) ÷ 3 = 1.5 y_2 = (2 × 2 + 2 ÷ 2^2) ÷ 3 = 1.5 y2​=(2×2+2÷22)÷3=1.5

y 3 = ( 2 × 1.5 + 2 ÷ 1. 5 2 ) ÷ 3 = 1.296296 y_3 = (2 × 1.5 + 2 ÷ 1.5^2) ÷ 3 = 1.296296 y3​=(2×1.5+2÷1.52)÷3=1.296296

y 4 = ( 2 × 1.296296 + 2 ÷ 1.29629 6 2 ) ÷ 3 = 1.260932 y_4 = (2 × 1.296296 + 2 ÷ 1.296296^2) ÷ 3 = 1.260932 y4​=(2×1.296296+2÷1.2962962)÷3=1.260932

y 5 = ( 2 × 1.260932 + 2 ÷ 1.26093 2 2 ) ÷ 3 = 1.259922 y_5 = (2 × 1.260932 + 2 ÷ 1.260932^2) ÷ 3 = 1.259922 y5​=(2×1.260932+2÷1.2609322)÷3=1.259922

经过多次迭代,计算出来的结果 2 3 ≈ 1.259922 {\sqrt[3]{2}} \approx 1.259922 32 ​≈1.259922。

使用牛顿迭代法计算立方根需要大量的浮点数运算,运算速度会远远弱于整数运算。

算法讲解

接下来解释该算法的核心i = (0x2a517d47 + (0.333f * i));

首先需要知道浮点数的基本概念。

32位浮点数基本概念

浮点数由三部分组成:符号,指数和尾数。

32位浮点数,用二进制表示

用公式表示就是

( − 1 ) s ( 1 + m ) 2 e (-1)^s(1+m)2^e (−1)s(1+m)2e

这里s是符号(sign),e是指数(exponent),m是尾数(mantissa)。

因为这里计算立方根只计算正数,负数可以先将负数转换成正数计算,之后再转换成负数。

所以从假设符号位是 0 ,公式简化为

( 1 + m ) 2 e (1+m)2^e (1+m)2e

指数部分 e e e的数值范围, − 127 ≤ e ≤ 128 -127 \leq e \leq 128 −127≤e≤128。

尾数部分 m m m的数值范围, 0 ≤ m < 1 0 \leq m < 1 0≤m<1。

如果将浮点数转换成整数时,整数的数值就是

M + L E M + LE M+LE

这里 E E E表示指数(exponent), M M M表示尾数(mantissa), L L L是 2 23 2^{23} 223。

将指数部分看做是整数,用 E E E来表示,那么范围是 0 ≤ E ≤ 255 0 \leq E \leq 255 0≤E≤255。 E E E如果减去127,范围变成-127 - 128,变成一个有符号数。

所以 E E E和 e e e的转换关系是 e = E − B e = E - B e=E−B, B B B是127。

如果将尾数部分看做是整数,用 M M M来表示。数值范围是 0 ≤ M < 2 23 0 \leq M < 2^{23} 0≤M<223。

所以 M M M和 m m m的转换关系是 m = M L m = \frac{M}{L} m=LM​, L L L是 2 23 2^{23} 223。

推导过程

接下来进入推导过程。

给定一个 x x x,计算立方根 y y y:

y = x 3 = x 1 3 y = {\sqrt[3]{x}} = x^{\frac 13} y=3x ​=x31​

首先对等式两边取以 2 为底的对数

log ⁡ 2 y = 1 3 log ⁡ 2 x \log_2 y = {\frac 13}\log_2 x log2​y=31​log2​x

将x和y用浮点数替换:

log ⁡ 2 ( ( 1 + m y ) 2 e y ) = 1 3 ( log ⁡ 2 ( ( 1 + m x ) 2 e x ) ) \log_2 ((1+m_y)2^{e_y}) = {\frac 13}(\log_2 ((1+m_x)2^{e_x})) log2​((1+my​)2ey​)=31​(log2​((1+mx​)2ex​))

log ⁡ 2 ( 1 + m y ) + e y = 1 3 ( log ⁡ 2 ( 1 + m x ) + e x ) \log_2 (1+m_y) + e_y = {\frac 13}(\log_2 (1+m_x) + e_x) log2​(1+my​)+ey​=31​(log2​(1+mx​)+ex​)

算式两边都有这样的项

log ⁡ 2 ( 1 + v ) \log_2(1 + v) log2​(1+v)

其中v的值范围0 < v v v < 1。

当v的取值在0到1之间时,这个函数和一条直线很接近:

方程式:

log ⁡ 2 ( 1 + v ) ≈ v + σ \log_2(1 + v) \approx v + \sigma log2​(1+v)≈v+σ

其中 σ \sigma σ是一个常数,可以通过调整这个常数让两个曲线更加近似。

上面的方程

log ⁡ 2 ( 1 + m y ) + e y = 1 3 ( log ⁡ 2 ( 1 + m x ) + e x ) \log_2 (1+m_y) + e_y = {\frac 13}(\log_2 (1+m_x) + e_x) log2​(1+my​)+ey​=31​(log2​(1+mx​)+ex​)

进行化简

m y + σ + e y ≈ 1 3 ( m x + σ + e x ) m_y + \sigma + e_y \approx {\frac 13}(m_x + \sigma + e_x) my​+σ+ey​≈31​(mx​+σ+ex​)

接下来用整形解释下的指数和尾数来替代浮点数解释:

M y L + σ + E y − B ≈ 1 3 ( M x L + σ + E x − B ) \frac{M_y}{L} + \sigma + E_y - B \approx {\frac 13}(\frac{M_x}{L} + \sigma + E_x - B) LMy​​+σ+Ey​−B≈31​(LMx​​+σ+Ex​−B)

进行化简

M y L + E y ≈ 1 3 ( M x L + σ + E x − B ) − σ + B \frac{M_y}{L} + E_y \approx {\frac 13}(\frac{M_x}{L} + \sigma + E_x - B) - \sigma + B LMy​​+Ey​≈31​(LMx​​+σ+Ex​−B)−σ+B

M y L + E y ≈ 1 3 ( M x L + E x ) − 2 3 ( σ − B ) \frac{M_y}{L} + E_y \approx {\frac 13}(\frac{M_x}{L} + E_x) - \frac{2}{3}(\sigma - B) LMy​​+Ey​≈31​(LMx​​+Ex​)−32​(σ−B)

M y + L E y ≈ 2 3 L ( B − σ ) + 1 3 ( M x + L E x ) M_y + LE_y \approx {\frac 23}L(B - \sigma) + {\frac 13}(M_x + LE_x) My​+LEy​≈32​L(B−σ)+31​(Mx​+LEx​)

两边都得到了整形解释的值:

I y ≈ 2 3 L ( B − σ ) + 1 3 I x \mathbf{I_y} \approx {\frac 23}L(B - \sigma) + {\frac 13}\mathbf{I_x} Iy​≈32​L(B−σ)+31​Ix​

其中 2 3 L ( B − σ ) {\frac 23}L(B - \sigma) 32​L(B−σ)是一个常数。

用代码表示就是:

i = K + i / 3;

K就是算法中的常数。通过选取合适的 σ \sigma σ的值,就能得到K值。

魔数的选定

计算魔数所用的 σ \sigma σ可以采用穷举搜索或者二分法寻找最优值。

关于 σ \sigma σ值的选定的详细内容,可以参考以下论文:

Lomont, Chris. Fast Inverse Square Root. February 2003.

McEniry, Charles. The Mathematics Behind the Fast Inverse Square Root Function Code . August 2007 .

这里选定的值为 σ \sigma σ = 0.0450465。

带入计算出K值:

K = 2 3 L ( B − σ ) = 2 3 2 23 ( 127 − 0.0450465 ) ≈ 709983559 = 0 x 2 a 517 d 47 K = {\frac 23}L(B - \sigma) = {\frac 23}{2^{23}}(127 - 0.0450465) \approx 709983559 = 0x2a517d47 K=32​L(B−σ)=32​223(127−0.0450465)≈709983559=0x2a517d47

这里的K值就是公式中的0x2a517d47。


参考资料

平方根倒数速算法
0x5f3759df


本文链接:https://blog.csdn.net/u012028275/article/details/113822421

【学习笔记】使用魔数快速求立方根相关推荐

  1. 【学习笔记】使用魔数快速求平方根

    [学习笔记]使用魔数快速求平方根 简介 介绍使用魔数0x1fbd1df5快速求平方根x{\sqrt{x}}x​的C语言实现和公式的推导. 代码 float MagicSqrt(float x) {fl ...

  2. JDBC学习笔记01【JDBC快速入门、JDBC各个类详解、JDBC之CRUD练习】

    黑马程序员-JDBC文档(腾讯微云)JDBC笔记.pdf:https://share.weiyun.com/Kxy7LmRm JDBC学习笔记01[JDBC快速入门.JDBC各个类详解.JDBC之CR ...

  3. Redis学习笔记①基础篇_Redis快速入门

    若文章内容或图片失效,请留言反馈.部分素材来自网络,若不小心影响到您的利益,请联系博主删除. 资料链接:https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA( ...

  4. OGG学习笔记04-OGG复制部署快速参考

    OGG学习笔记04-OGG复制部署快速参考 源端:Oracle 10.2.0.5 RAC + ASM 节点1 Public IP地址:192.168.1.27 目标端:Oracle 10.2.0.5 ...

  5. Servlet和HTTP请求协议-学习笔记01【Servlet_快速入门-生命周期方法、Servlet_3.0注解配置、IDEA与tomcat相关配置】

    Java后端 学习路线 笔记汇总表[黑马程序员] Servlet和HTTP请求协议-学习笔记01[Servlet_快速入门-生命周期方法.Servlet_3.0注解配置.IDEA与tomcat相关配置 ...

  6. Python学习笔记--10.Django框架快速入门之后台管理admin(书籍管理系统)

    Python学习笔记--10.Django框架快速入门之后台管理 一.Django框架介绍 二.创建第一个Django项目 三.应用的创建和使用 四.项目的数据库模型 ORM对象关系映射 sqlite ...

  7. UE4学习笔记1st:编程快速入门

    UE4学习笔记1st:编程快速入门 今天我开始学习虚幻4游戏引擎,为了此我专门买了新的电脑,我将主要配置写在这里,有想学习的同学可以参考 显卡:丽台K620 CPU:E3-1230-V3 主板:b85 ...

  8. matlab修改变量名称_MATLAB学习笔记1:如何快速创建多个仅有数字变化变量名?...

    一直以来,本人用MATLAB都是想用什么功能就搜索什么功能,或者查看MATLAB帮助文档.(不得不说MATLAB的帮助文档做得真好) 由于没有系统学习过MATLAB,所以代码都很水-- 好吧,开个文章 ...

  9. 视觉SLAM十四讲学习笔记-第四讲-李代数求导与扰动模型

    专栏系列文章如下: 视觉SLAM十四讲学习笔记-第一讲_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习笔记-第二讲-初识SLAM_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习 ...

最新文章

  1. windows无法配置此无线连接_Kubernetes 1.18功能详解:OIDC发现、Windows节点支持,还有哪些新特性值得期待?...
  2. Android自定义View:MeasureSpec的真正意义与View大小控制
  3. springboot继承组件_SpringBoot如何扩展引入的组件,以及如何自动配置组件原理
  4. VR开发中性能问题—OculusWaitForGPU
  5. java Arrays Generic
  6. 双列集合Map的实现类
  7. 王自如、罗永浩将一起出镜直播带货?罗永浩亲自回应
  8. 基于JAVA+SpringMVC+Mybatis+MYSQL的高校科研管理系统
  9. 【Java并发编程】之十六:深入Java内存模型——happen-before规则及其对DCL的分析(含代码)...
  10. 360安全卫士v3.0beta3版发布!
  11. [转载] Python 天气 简单 数据分析及可视化
  12. 数据结构之链表(Linked list)
  13. Android模拟器读取GPS串口模拟器GPS数据
  14. DOS编写脚本常用命令整理
  15. maven命令行打包
  16. 物联网服务器搭建记录,心得
  17. 临床试验数据管理系统
  18. 计算机睡眠功能命令,使用WINDOWS命令行进入睡眠模式
  19. 马斯克在推特说特斯拉股价太高导致大跌 会被罚吗
  20. CKfinder3版本冲突

热门文章

  1. 用visio将.vsdx格式转换为.eps格式
  2. 查看iOS设备UDID
  3. Cocos 十年 | 业界大佬齐送祝福,同心至远方
  4. 【翻译】Context should go away for Go 2
  5. SwaggerUI初探
  6. 实验笔记之——covariance matrix pooling
  7. Windows 电脑杀毒简单有效的方式
  8. win全盘重分- DiskGenius
  9. Android中关于notifyDataSetChanged()方法的注意
  10. 统计学习——简单阐述显著性水平α、p-value之间的关系