zoukankan      html  css  js  c++  java
  • [Swift]快速反向平方根 | Fast inverse square root

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
    ➤微信公众号:山青咏芝(shanqingyongzhi)
    ➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/
    ➤GitHub地址:https://github.com/strengthen/LeetCode
    ➤原文地址:https://www.cnblogs.com/strengthen/p/10989143.html 
    ➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
    ➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ 

    曲面法线广泛用于光照和阴影计算,需要计算矢量的范数。这里显示了垂直于表面的矢量场。

     

    使用法线C从入射角求出反射角的二维例子; 

    在这种情况下,在从曲面镜反射的光上。快速反平方根用于将该计算推广到三维空间。

           浮点数的倒数平方根用于计算标准化向量。程序可以使用标准化向量来确定入射角和反射角。3D图形程序必须每秒执行数百万次这些计算以模拟光照。当代码在20世纪90年代早期开发时,大多数浮点处理能力都落后于整数处理的速度。在专门用于处理变换和照明的硬件出现之前,这对3D图形程序来说很麻烦。

           为了计算平方根倒数的一般方法是计算一个近似为1x,然后通过另一种方法修改该近似直到它来实际结果的可接受的误差范围之内。20世纪90年代早期的常用软件方法从查找表中得出近似值。快速平方根的关键是通过利用浮点数的结构直接计算近似,证明比表查找更快。该算法比使用另一种方法计算平方根并计算倒数通过浮点除法快大约四倍。该算法的设计考虑了IEEE 754-1985 32位浮点规范,但研究表明它可以在其他浮点规范中实现。

           快速反平方根的速度优势来自于将包含浮点数的长字视为整数,然后从特定常数0x5F3759DF中减去它。查看代码的人不会立即清楚常量的目的,因此,与代码中的其他此类常量一样,它通常被称为幻数。该整数减法和位移产生长字,当作为浮点数处理时,该长字是输入数的反平方根的粗略近似。执行牛顿方法的一次迭代以获得一定的准确性,并且代码完成。该算法使用Newton方法的唯一第一近似值生成相当准确的结果; 然而,它比在1999年发布的rsqrtss x86处理器上使用SSE指令要慢得多且准确度低得多。

    Talk is cheap.Show me your code:

    (1)、平方根函数:方法1-牛顿迭代法

    
    
     1 func sqrt1(_ x: Float) -> Float {
     2     //判断x是否为0
     3     if x == 0 {return 0}
     4     let high:Float = Float(x)
     5     let mid:Float = high/2
     6     var y:Float = (mid>1) ? mid : 1
     7     while(true)
     8     {
     9         let dy:Float = (y * y + high) / Float(y)/2
    10         //处理float类型的取绝对值fabsf(float i)
    11         if fabsf(y - dy) <= 0.00000001
    12         {
    13             return dy
    14         }
    15         else
    16         {
    17             y = dy
    18         }
    19     }
    20 }

    (2)、平方根函数:方法2 

     1 func sqrt2(_ x:Float) -> Float
     2 {
     3     var y:Float
     4     var delta:Float
     5     var maxError:Float
     6     if x <= 0 {return 0}
     7     // initial guess
     8     y = x / 2
     9     // refine
    10     maxError = x * 0.00000001
    11     repeat {
    12         delta = ( y * y ) - x
    13         y -= delta / ( 2 * y )
    14     } while ( delta > maxError || delta < -maxError )
    15     return y
    16 }

    (3)、快速反向平方根:方法1-使用memcpy()

    注意开头的导入:import Foundation。

    invSqrt(x)比1.0/sqrt(x)快速增加了约40%,

    最大相对误差低于0.176% 

     1 import Foundation
     2 func invSqrt1(_ x: Float) -> Float {
     3     let halfx = 0.5 * x
     4     var y = x
     5     var i : Int32 = 0
     6     memcpy(&i, &y, 4)
     7     i = 0x5f3759df - (i >> 1)
     8     memcpy(&y, &i, 4)
     9     y = y * (1.5 - (halfx * y * y))
    10     return y
    11 }

    (4)、快速反向平方根:方法2-bitPattern()

    从Swift3开始,memcpy()可以用bitPattern:方法替换Float相应的构造函数UInt32:

    1 func invSqrt2(_ x: Float) -> Float {
    2     let halfx = 0.5 * x
    3     var i = x.bitPattern
    4     i = 0x5f3759df - (i >> 1)
    5     var y = Float(bitPattern: i)
    6     y = y * (1.5 - (halfx * y * y))
    7     return y
    8 }

    综合(1)~(4)测试: 

     1 let x:Float = 10.0
     2 let ans1:Float = sqrt1(x)
     3 let ans2:Float = sqrt2(x)
     4 let ans3:Float = invSqrt1(x)
     5 let ans4:Float = invSqrt2(x)
     6 print(1.0/ans1)
     7 //Print 0.31622776
     8 print(1.0/ans2)
     9 //Print 0.31622776
    10 print(ans3)
    11 //Print 0.31568578
    12 print(ans4)
    13 //Print 0.31568578

    常量0x5f3759df来自哪里?为什么代码有效?

    快速逆平方根:有时被称为快速InvSqrt()或由十六进制常数0x5F3759DF的估计1√x算法的倒数(或乘法逆)的的平方根的32位的浮点数X在IEEE 754浮点格式。此操作用于数字信号处理以标准化矢量,即将其缩放为长度1.例如,计算机图形程序使用反平方根来计算入射角和反射对照明和阴影。

           该算法接受32位浮点数作为输入,并存储一半的值供以后使用。然后处理代表浮点数作为一个32位的整数的比特,一个逻辑移位一个位权执行,并且结果从减去幻数 0X 5F3759DF,这是一个近似的浮点表示√2127。这导致输入的平方根的第一近似。再次将这些位作为浮点数处理,它运行牛顿方法的一次迭代,产生更精确的近似。

    一个有效例子:作为一个例子,数x = 0.15625可用于计算1⁄√x ≈ 2.52982.。该算法的第一步如下所示: 

    1 0011_1110_0010_0000_0000_0000_0000_0000  Bit pattern of both x and i
    2 0001_1111_0001_0000_0000_0000_0000_0000  Shift right one position: (i >> 1)
    3 0101_1111_0011_0111_0101_1001_1101_1111  The magic number 0x5F3759DF
    4 0100_0000_0010_0111_0101_1001_1101_1111  The result of 0x5F3759DF - (i >> 1)

    使用IEEE 32位表示: 

    1 0_01111100_01000000000000000000000  1.25 × 23
    2 0_00111110_00100000000000000000000  1.125 × 265
    3 0_10111110_01101110101100111011111  1.432430... × 263
    4 0_10000000_01001110101100111011111  1.307430... × 21

    将该最后一位模式重新解释为浮点数给出近似值y = 2.61486,其具有大约3.4%的误差。在牛顿方法的一次迭代之后,最终结果是y = 2.52549,误差仅为0.17%。

    算法描述:快速反向平方根算法计算1x,通过执行以下步骤。

    (1)、将参数x别名为整数,作为计算 log2(x)近似值的方法

    (2)、使用这种近似计算的近似对数log2(1x)= −1/2 log2(x)

    (3)、别名返回浮点数,作为计算base-2指数近似值的方法

    (4)、使用牛顿方法的单次迭代来细化近似。

    准确度:下图显示启发式快速反平方根与libstdc提供的平方根直接反转之间差异的图表。注意两个轴上的对数刻度。如上所述,近似值令人惊讶地准确。右边的图表描绘了函数的误差(即,通过运行牛顿方法的一次迭代后的近似值的误差),对于从0.01开始的输入,其中标准库给出10.0作为结果,而InvSqrt()给出9.982522,使差值为0.017479,即真值的0.175%。绝对误差仅从此开始下降,而相对误差保持在所有数量级的相同范围内。

  • 相关阅读:
    机器学习-数据与特征工程
    机器学习-聚类(clustering)算法:K-means算法
    机器学习-回归中的相关度和R平方值
    机器学习-非线性回归(Logistic Regression)及应用
    机器学习-多元线性回归(一)
    机器学习-简单线性回归(二)
    MVC,MVP,MVVM
    Git从入门到熟练
    NSNotificationCenter
    FMDB的线程安全
  • 原文地址:https://www.cnblogs.com/strengthen/p/10989143.html
Copyright © 2011-2022 走看看