为了账号安全,请及时绑定邮箱和手机立即绑定

略有不同的浮点数学结果(C 到 golang)

略有不同的浮点数学结果(C 到 golang)

Go
慕标琳琳 2021-11-08 10:08:46
我正在直接在 golang 中开发一个技术指标库。除其他外,它是学习 golang 的练习。我一直在通过使用 TA-Lib(或者更确切地说是围绕 TA-Lib 的 ruby 包装器)生成的数据构建测试用例来验证我的算法的结果。在我开始实施布林带之前,这一直运行良好。我的实现似乎工作正常,但在小数点后 14-15 位有所不同。我已经阅读了不同编程语言中的 Floating point math并怀疑这可能是罪魁祸首(我以稍微不同的顺序进行计算)。编辑添加:上面的问题涉及浮点数学的一个非常简单的表现形式。确认一段较长的代码实际上解决了这个问题要困难得多。由于顺序,我如何确认它只是浮点数学的变化?/ 结束编辑我的理解正确吗?这是我的实现:package taimport (  "math")func BollingerBands(values []float64, period int) ([]float64, []float64, []float64) {  deviationsUp := 2.0  deviationsDown := 2.0  middleBand := Sma(values, period)  offset := len(values)-len(middleBand)  var upperBand []float64  var lowerBand []float64  for idx, v := range middleBand {    backIdx := offset+idx-period+1    curIdx := offset+idx+1    if backIdx < 0 {      backIdx = 0    }    stdDev := SliceStdDev(values[backIdx:curIdx])    upperBand = append(upperBand, v + (stdDev * deviationsUp))    lowerBand = append(lowerBand, v - (stdDev * deviationsDown))  }  return upperBand, middleBand, lowerBand}// Sma produces the Simple Moving Average for the// supplied array of float64 values for a given periodfunc Sma(values []float64, period int) []float64{  var result []float64  for index,_ := range values {    indexPlusOne := index+1    if(indexPlusOne>=period) {      avg := Mean(values[indexPlusOne-period:indexPlusOne])      result = append(result, avg)    }  }  return result}// SliceMean returns the Mean of the slice of float64func SliceMean(values []float64) float64 {  var total float64=0    for _,element := range values {        total += element    }  return total / float64(len(values))}// SliceVariance returns the variance of the slice of float64.func SliceVariance(values []float64) float64 {    if 0 == len(values) {        return 0.0    }    m := SliceMean(values)    var sum float64    for _, v := range values {        d := v - m        sum += d * d    }    return sum / float64(len(values))}
查看完整描述

2 回答

?
jeck猫

TA贡献1909条经验 获得超7个赞

我认为算法是不同的。例如方差:


/* Do the MA calculation using tight loops. */

/* Add-up the initial periods, except for the last value. */

periodTotal1 = 0;

periodTotal2 = 0;

trailingIdx = startIdx-nbInitialElementNeeded;


i=trailingIdx;

if( optInTimePeriod > 1 )

{

   while( i < startIdx ) {

      tempReal = inReal[i++];

      periodTotal1 += tempReal;

      tempReal *= tempReal;

      periodTotal2 += tempReal;

   }

}


/* Proceed with the calculation for the requested range.

 * Note that this algorithm allows the inReal and

 * outReal to be the same buffer.

 */

outIdx = 0;

do

{

   tempReal = inReal[i++];


   /* Square and add all the deviation over

    * the same periods.

    */


   periodTotal1 += tempReal;

   tempReal *= tempReal;

   periodTotal2 += tempReal;


   /* Square and add all the deviation over

    * the same period.

    */


   meanValue1 = periodTotal1 / optInTimePeriod;

   meanValue2 = periodTotal2 / optInTimePeriod;


   tempReal = inReal[trailingIdx++];

   periodTotal1 -= tempReal;

   tempReal *= tempReal;

   periodTotal2 -= tempReal;


   outReal[outIdx++] = meanValue2-meanValue1*meanValue1;

} while( i <= endIdx );

这看起来不像你的方差。如果您要重现算法以便它们执行完全相同的操作,那么 Go 版本应该产生相同的结果。Go 只是在做标准的 IEEE 754 浮点运算。


至于“顺序重要吗?”这个问题。确实如此。由于浮点算术不精确,您在计算时会丢失信息。大多数时候它不会产生太大的不同,但有时算法可能非常容易受到这些变化的影响。(因此以代数方式重新排列您的公式可能不会在实际代码中得到相同的答案)


您经常会在这些库中发现算法旨在解决这些问题,因此它们通常看起来不像是幼稚的实现。例如mean,通常是一个微不足道的函数,但以下是它在 GSL 中的计算方式:


double

FUNCTION (gsl_stats, mean) (const BASE data[], const size_t stride, const size_t size)

{

  /* Compute the arithmetic mean of a dataset using the recurrence relation 

     mean_(n) = mean(n-1) + (data[n] - mean(n-1))/(n+1)   */


  long double mean = 0;

  size_t i;


  for (i = 0; i < size; i++)

    {

      mean += (data[i * stride] - mean) / (i + 1);

    }


  return mean;

}

因此,除非您完全匹配算法,否则您的答案将略有不同。(这并不一定意味着你的程序是错误的)


通常用于此的一种解决方案是在一个非常小的数字(math.Abs(expected-result) < ɛ,您定义 ɛ: 的地方const ɛ = 0.0000001)内进行相等比较,而不是使用==.


查看完整回答
反对 回复 2021-11-08
?
临摹微笑

TA贡献1982条经验 获得超2个赞

正如 Caleb 和 Matteo 的评论/答案所建议的那样,即使代码排序方式的细微差异也会导致浮点值的差异。

我最终确认,至少在一个小样本量的情况下,完全像 TA-Lib 一样实现代码会产生正确的浮点值。正如预期的那样,即使稍微偏离 TA-Lib (C) 实现也会导致浮点值的微小差异。



查看完整回答
反对 回复 2021-11-08
  • 2 回答
  • 0 关注
  • 186 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信