浮点数学结果略有不同(C到golang)
我正致力于直接在golang开发一个技术指标库。除其他外,这是一次学习格朗的练习 我一直在用TA-Lib(或者更确切地说是TA-Lib周围的ruby包装器)生成的数据构建测试用例,以验证算法的结果 这一直运作良好,直到我得到布林格乐队的实施。我的实现似乎工作得很好,但在小数点后14-15位有所不同 我已经阅读并怀疑这可能是罪魁祸首(我以稍微不同的顺序进行计算) 编辑以添加: 上面的问题涉及浮点数学的一个非常简单的表现形式。要确认更长的代码是否真的遇到了这个问题要困难得多 我怎样才能确认这仅仅是因为顺序而引起的浮点数学的变化呢 /End编辑 我的理解正确吗 以下是我的实现:浮点数学结果略有不同(C到golang),go,floating-point-precision,Go,Floating Point Precision,我正致力于直接在golang开发一个技术指标库。除其他外,这是一次学习格朗的练习 我一直在用TA-Lib(或者更确切地说是TA-Lib周围的ruby包装器)生成的数据构建测试用例,以验证算法的结果 这一直运作良好,直到我得到布林格乐队的实施。我的实现似乎工作得很好,但在小数点后14-15位有所不同 我已经阅读并怀疑这可能是罪魁祸首(我以稍微不同的顺序进行计算) 编辑以添加: 上面的问题涉及浮点数学的一个非常简单的表现形式。要确认更长的代码是否真的遇到了这个问题要困难得多 我怎样才能确认这仅仅是因
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))
}
ta包
进口(
“数学”
)
func bollingerband(值[]float64,句点int)([]float64,[]float64,[]float64){
偏差SUP:=2.0
偏差下降:=2.0
中间带:=Sma(值、周期)
偏移量:=len(值)-len(中间带)
变量上限[]浮点64
变量lowerBand[]浮点64
对于idx,v:=范围中间带{
backIdx:=偏移量+idx周期+1
curIdx:=偏移量+idx+1
如果backIdx<0{
backIdx=0
}
stdDev:=SliceStdDev(值[backIdx:curIdx])
上限=附加(上限,v+(标准差*偏差上限))
lowerBand=附加(lowerBand,v-(stdDev*偏差向下))
}
返回上带、中带、下带
}
//Sma生成该区域的简单移动平均线
//为给定期间提供的浮点64值数组
func Sma(值[]浮点64,句点整数)[]浮点64{
var结果[]浮点64
对于索引,\:=范围值{
indexPlusOne:=索引+1
if(indexPlusOne>=期间){
平均值:=平均值(值[指数周期:指数周期])
结果=追加(结果,平均值)
}
}
返回结果
}
//SliceMean返回浮点64切片的平均值
func SliceMean(值[]float64)float64{
var total float64=0
对于u,元素:=范围值{
总数+=元素
}
返回总计/浮动64(len(值))
}
//SliceVariance返回浮点64切片的方差。
func SliceVariance(值[]float64)float64{
如果0==len(值){
返回0.0
}
m:=平均值(值)
var和浮动64
对于v,v:=范围值{
d:=v-m
总和+=d*d
}
返回和/浮动64(len(值))
}
//SliceStdDev返回float64切片的标准偏差。
func SliceStdDev(值[]float64)float64{
返回math.Sqrt(方差(值))
}
这将导致上层频带的以下值:[94.925647305992991、94.505888827974477、92.12752961253167、101.58367006802706、114.6433137907878675、120.580881180322]
使用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>}
需要“指示器/混音器”
x=[26.0,54.0,8.0,77.0,61.0,39.0,44.0,91.0,98.0,17.0]
y=x.指示器(:bbands_5)
#{:out_real_upperØband=>[94.9256473059929,94.505888827974477,92.12752961253167,101.58367006802709,114.643313790708678,120.580881180323,nil,nil,nil,nil]}
我认为算法是不同的。例如:
因此,除非你完全匹配算法,否则你的答案会有微妙的不同。(这并不一定意味着你的程序是错误的)
通常使用的一种解决方案是在非常小的数字内进行等式比较(
math.Abs(预期结果)<ɛ
,其中定义了ɛ:constɛ=0.0000001
),而不是使用Caleb和Matteo的评论/回答所建议的=
,即使代码排序方式上的细微差异也会导致浮点值的差异
我最后确认,至少在小样本的情况下,实现与TA Lib完全相同的代码会产生正确的浮点值。正如所料,即使稍微偏离TA Lib(C)实现,也会导致浮点值的微小差异。这不是答案,但如果您想排除显示(舍入)浮点值作为差异原因的可能性,你可以考虑打印两个十六进制,并比较它。如果你不按完全相同的顺序执行操作,则完全期望在结果上得到类似的差异。此外,根据编译器使用浮点寄存器的方式,您可能会得到不同的结果(通常x86的C代码被编译为“意外地”利用x87寄存器的全部80位精度,因此如果您一直舍入到常规的64位,则会得到不同的舍入)。@WanderNauta似乎是一个答案。Thanks@MatteoItalia谢谢以不同的方式取整并没有什么大问题,只要我理解为什么可能重复感谢伟大的答案,以及感谢
(math.Abs(expected result)<ɛ
,我会调整测试用例以使用它。
/* 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 );
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;
}