Swift CORDIC 算法给出不变的答案

Swift CORDIC Algorithm gives constant answer

我尝试在 Cordic wikipedia webpage

上翻译 MATLAB 语言

但是当我输入这些时:

print(cordic(beta: Double.pi/9, n: 20))
print(cordic(beta: Double.pi/8, n: 20))

我明白了

[-0.17163433840184755, 0.98516072489744066]
[-0.17163433840184755, 0.98516072489744066]

它总是给我一个不变的答案。为什么?我确定 "angle" 和 "Kvalues" 数组计算正确。

代码如下:

import Foundation

var angles: [Double] = []

for i: Double in stride(from: 0, to: 27, by: 1) {
    angles.append(atan(pow(2, -i)))
}
var Kvalues: [Double] = []

for i: Double in stride(from: 0, to: 23, by: 1) {
    Kvalues.append(1/sqrt(abs(Double(1) + pow(2,-2 * i))))
    if i > 0 {
        Kvalues[Kvalues.count - 1] *= Kvalues[Kvalues.count - 2]
    }
}
func min(_ a: Int, _ b: Int) -> Int {
    return a > b ? b : a
}
func cordic(beta: Double, n: Int) -> [Double] {
    var beta1 = beta
    let Kn = Kvalues[min(n, Kvalues.count - 1)]
    var v: [Double] = [1,0]
    var poweroftwo: Double = 1
    var angle = angles[0]

    for j in 0 ..< n {
        let sigma: Double = beta < 0 ? -1 : 1
        let factor: Double = sigma * poweroftwo
        v = [v[0] - v[1] * factor, v[1] + v[0] * factor]
        beta1 -= sigma * angle
        poweroftwo /= 2
        angle = j + 2 > angles.count ? angle / 2 : angles[j + 2]
    }
    return [v[0] * Kn, v[1] * Kn]
}
print(cordic(beta: Double.pi/9, n: 20))
print(cordic(beta: Double.pi/8, n: 20))

不同的输入会得到相同的结果,因为在

let sigma: Double = beta < 0 ? -1 : 1

beta应该是beta1,也就是局部变量就是 循环更新。

但即使修复后结果也不正确,那就是 由两个 "off-by-one" 索引错误引起。 算法中的数组 description 是从 1 开始的,Swift 数组是从 0 开始的。所以

let Kn = Kvalues[min(n, Kvalues.count - 1)]
// should be
let Kn = Kvalues[min(n-1, Kvalues.count - 1)]

angle = j + 2 > angles.count ? angle / 2 : angles[j + 2]
// should be
angle = j + 1 >= angles.count ? angle / 2 : angles[j + 1]

应该为 i 定义 anglesKvalues 数组,范围从 0 到 ,包括 27 个。 23.

最后,您无需定义自己的 min 函数,因为 Swift 标准库中已有该函数。

将它们放在一起你的代码将是:

var angles: [Double] = []

for i: Double in stride(from: 0, through: 27, by: 1) {
    angles.append(atan(pow(2, -i)))
}
var Kvalues: [Double] = []

for i: Double in stride(from: 0, through: 23, by: 1) {
    Kvalues.append(1/sqrt(abs(Double(1) + pow(2,-2 * i))))
    if i > 0 {
        Kvalues[Kvalues.count - 1] *= Kvalues[Kvalues.count - 2]
    }
}

func cordic(beta: Double, n: Int) -> [Double] {
    var beta1 = beta
    let Kn = Kvalues[min(n-1, Kvalues.count - 1)]
    var v: [Double] = [1,0]
    var poweroftwo: Double = 1
    var angle = angles[0]

    for j in 0 ..< n {
        let sigma: Double = beta1 < 0 ? -1 : 1
        let factor: Double = sigma * poweroftwo
        v = [v[0] - v[1] * factor, v[1] + v[0] * factor]
        beta1 -= sigma * angle
        poweroftwo /= 2
        angle = j + 1 >= angles.count ? angle / 2 : angles[j + 1]
    }
    return [v[0] * Kn, v[1] * Kn]
}

这会产生很好的近似值:

print(cordic(beta: Double.pi/9, n: 20)) // [0.93969210812600046, 0.34202155184390554]
print(cordic(beta: Double.pi/8, n: 20)) // [0.92388022188807306, 0.38268176805806309]

准确值为

print(cos(Double.pi/9), sin(Double.pi/9)) // 0.939692620785908 0.342020143325669
print(cos(Double.pi/8), sin(Double.pi/8)) // 0.923879532511287 0.38268343236509