vDSP_conv 偶尔 returns NAN

vDSP_conv occasionally returns NANs

我正在使用 vDSP_conv 执行自相关。大多数情况下它工作得很好,但有时它会用 NaN 填充输出数组。

代码:

func corr_test() {
  var pass = 0

  var x = [Float]()
  for i in 0..<2000 {
    x.append(Float(i))
  }

  while true {
    print("pass \(pass)")

    let corr = autocorr(x)
    if corr[1].isNaN {
        print("!!!")
    }
    pass += 1
  }
}

func autocorr(a: [Float]) -> [Float] {
    let resultLen = a.count * 2 + 1
    let padding = [Float].init(count: a.count, repeatedValue: 0.0)
    let a_pad = padding + a + padding
    var result = [Float].init(count: resultLen, repeatedValue: 0.0)

    vDSP_conv(a_pad, 1, a_pad, 1, &result, 1, UInt(resultLen), UInt(a_pad.count))

    return result
}

输出:

pass ...
pass 169
pass 170
pass 171
(lldb) p corr
([Float]) $R0 = 4001 values {
  [0] = 2.66466637E+9
  [1] = NaN
  [2] = NaN
  [3] = NaN
  [4] = NaN
...

我不确定这里发生了什么。我认为我正在正确处理 0 填充,因为如果我不正确,我认为我不会在 99% 的时间内得到正确的结果。

想法?谢谢。

想通了。关键是来自 https://developer.apple.com/library/mac/samplecode/vDSPExamples/Listings/DemonstrateConvolution_c.html 的评论:

// “The signal length is padded a bit. This length is not actually passed to the vDSP_conv routine; it is the number of elements
// that the signal array must contain. The SignalLength defined below is used to allocate space, and it is the filter length
// rounded up to a multiple of four elements and added to the result length. The extra elements give the vDSP_conv routine
// leeway to perform vector-load instructions, which load multiple elements even if they are not all used. If the caller did not
// guarantee that memory beyond the values used in the signal array were accessible, a memory access violation might result.”

“填充了一点。”谢谢你这么具体。无论如何,这是最终的工作产品:

func autocorr(a: [Float]) -> [Float] {
let filterLen = a.count
let resultLen = filterLen * 2 - 1
let signalLen = ((filterLen + 3) & 0xFFFFFFFC) + resultLen

let padding1 = [Float].init(count: a.count - 1, repeatedValue: 0.0)
let padding2 = [Float].init(count: (signalLen - padding1.count - a.count), repeatedValue: 0.0)
let signal = padding1 + a + padding2

var result = [Float].init(count: resultLen, repeatedValue: 0.0)

vDSP_conv(signal, 1, a, 1, &result, 1, UInt(resultLen), UInt(filterLen))

// Remove the first n-1 values which are just mirrored from the end so that [0] always has the autocorrelation.
result.removeFirst(filterLen - 1)

return result
}

请注意,此处的结果未标准化。