美文网首页
Swift Double nan,infinity的问题

Swift Double nan,infinity的问题

作者: lucas_ljj | 来源:发表于2020-12-02 10:32 被阅读0次

最近在项目中遇到一个被nan坑惨的问题,记录一下
最开始的代码如下


/**

     * 辛普森积分法

     * @param start  积分区间起点

     * @param end  积分区间终点

     * @param St  t时刻的正股股价

     * @param K  行权价strike

     * @param R  无风险利率

     * @param T  time to maturity

     * @param V σ,正股年度波动率

     */

    static func SimpsonIntegrator(start: Double, end: Double, St: Double, R: Double, V: Double, T: Double) -> Double {
        let s = max(start, 0.000000001)
        let params = (St: St, R: R, V: V, T: T)
        if s >= end {
            return calculProb(K: end, P: params)
        }
        let ans = simpson(L: s, R: end, P: params)
        let mid = (s + end) / 2.0
        let left = simpson(L: s, R: mid, P: params)
        let right = simpson(L: mid, R: end, P: params)
        if fabs(ans - ( left + right)) < EPS {
            return ans
        }
        else {
            let p1 = SimpsonIntegrator(start: s, end: mid, St: St, R: R, V: V, T: T, step: theStep)
            let p2 = SimpsonIntegrator(start: mid, end: end, St: St, R: R, V: V, T: T, step: theStep)
            return p1 + p2
        }
        
    }

是一段很普通的辛普森积分法的函数。从c语言直接翻译过来的。是通过二分法递归调用求积分的近似值。我们测试过后结果正常以后就上线了。但是上线以后部分用户反馈手机发热严重。极少数用户甚至出现了网络返回全无的情况。我们开始排查。最后发现问题出现在了这个方法。
这个方法是在子线程执行的。但是线上有一些不正常的数据会出现0/0或者开负数根号的情况。会在运算过程中出现Double.Nan。当出现nan的参数时

if fabs(ans - ( left + right)) < EPS 

这个逃逸条件的判断始终为false。整个递归进入死循环。会始终占据一个子线程而不释放。当CPU除主线程的其他所有物理线程都被这个死循环占满后,网络请求回调的子线程都不会被执行。这是相当严重的问题了。

实际上当出现nan结果时。所有和Nan相关的判断都会始终未false

let num = Double.NaN
if num == num {
    print("Num is \(num)")
} else {
    print("NaN")
}
// 输出:
// NaN

随后我们修改了这个积分函数

/**
     * 辛普森积分法
     * @param start  积分区间起点
     * @param end  积分区间终点
     * @param St  t时刻的正股股价
     * @param K  行权价strike
     * @param R  无风险利率
     * @param T  time to maturity
     * @param V σ,正股年度波动率
     * step 默认最多递归2^16层
     */
    static func SimpsonIntegrator(start: Double, end: Double, St: Double, R: Double, V: Double, T: Double, step: Int = 16) -> Double {
        let s = max(start, 0.000000001)
        let params = (St: St, R: R, V: V, T: T)
        let theStep = step - 1
        if s >= end || theStep <= 0 {
            return calculProb(K: end, P: params)
        }
        let ans = simpson(L: s, R: end, P: params)
        let mid = (s + end) / 2.0
        let left = simpson(L: s, R: mid, P: params)
        let right = simpson(L: mid, R: end, P: params)
        if ans.isNaN || left.isNaN || right.isNaN {
            return 0
        }
        if fabs(ans - ( left + right)) < EPS {
            return ans
        }
        else {
            let p1 = SimpsonIntegrator(start: s, end: mid, St: St, R: R, V: V, T: T, step: theStep)
            let p2 = SimpsonIntegrator(start: mid, end: end, St: St, R: R, V: V, T: T, step: theStep)
            return p1 + p2
        }
    }

把Nan的情况排除掉,同时在递归增加一个层数限制,默认最多递归2^16层。
在进行复制且不熟悉的复杂运算时。一定要把Nan和 infinity的情况计算在内。同时递归算法应该要上一个保险的递归限制。就算计算有问题也不能出现死循环占满线程的情况。不能把错误扩大。

相关文章

网友评论

      本文标题:Swift Double nan,infinity的问题

      本文链接:https://www.haomeiwen.com/subject/yqojwktx.html