2015-09-05 69 views
2

我正在努力直接在golang开发一个技术指标库。除其他外,这是一次学习golang的练习。稍微不同的浮点数学结果(C去golang)

我一直在验证我的算法的结果,通过用TA-Lib生成的数据构建测试用例(或者是围绕TA-Lib的ruby包装)。

这一直工作得很好,直到我得到了布林带的实施。我的实现似乎工作正常,但在小数点后14-15位有所不同。

我读过Floating point math in different programming languages,并怀疑这可能是罪魁祸首(我按照稍微不同的顺序进行计算)。

编辑补充:

以上的浮点运算的一个非常简单的表现形式交易的问题。要确认一段较长的代码实际上是否碰到了这个问题要困难得多。

我该如何确认它仅仅是因为顺序的浮点数学变化?

/编辑完

我是在我的理解是否正确?

这是我实现:

package ta 

import (
    "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 period 
func 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 float64 
func 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)) 
} 

// SliceStdDev returns the standard deviation of the slice of float64. 
func SliceStdDev(values []float64) float64 { 
    return math.Sqrt(SliceVariance(values)) 
} 

这会导致下列值作为上带<[]float64 | len:6, cap:8>: [94.92564730599291, 94.50588827974477, 92.12752961253167, 101.58367006802706, 114.64331379078675, 120.58088881180322]

使用Ruby:

require 'indicator/mixin' 
x = [26.0, 54.0, 8.0, 77.0, 61.0, 39.0, 44.0, 91.0, 98.0, 17.0] 
y = x.indicator(:bbands_5) 
# {:out_real_upper_band=>[94.9256473059929, 94.50588827974477, 92.12752961253167, 101.58367006802709, 114.64331379078678, 120.58088881180323, nil, nil, nil, nil] <SNIP>} 
+3

这不是一个答案,但如果您想排除显示(四舍五入)浮点值作为差异的原因,您可以考虑将它们都打印为十六进制并进行比较。 –

+4

如果您不按照完全相同的顺序执行操作,则完全预计结果中会出现类似的差异。另外,根据编译器如何使用浮点寄存器,您可能会得到不同(通常x86的C代码被编译为“意外”利用x87寄存器的全部80位精度,因此如果您绕过常规64位)。 –

+0

@WanderNauta看起来像一个答案。谢谢 –

回答

2

我想的算法不同。例如,variance

/* 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)而不是使用==

+0

感谢您的好评,并且'(math.Abs​​(expected-result)<ɛ',将调整我的测试用例以使用它。 –

0

正如Caleb和Matteo的评论/回答所建议的,代码排序方式中的细微差异导致浮点值的差异。

我已经结束了确认,至少是一个小样本大小,完全像TA-Lib一样执行代码导致正确的浮点值。如预期的那样,与TA-Lib(C)的实现略有偏差会导致浮点值的细微差异。