InDelta和InEpsilon之间的区别是什么?

huangapple go评论141阅读模式
英文:

Difference between InDelta and InEpsilon

问题

这两个函数的作用是相同的,都用于比较两个数值是否在一定的误差范围内。它们的区别在于计算误差的方式不同。

InDelta函数使用的是绝对误差(absolute error)的方式,通过比较两个数值的差值是否小于给定的误差范围(delta)来判断它们是否相等。

InEpsilon函数使用的是相对误差(relative error)的方式,通过计算两个数值的相对误差是否小于给定的误差范围(epsilon)来判断它们是否相等。

选择使用哪个函数取决于具体的使用场景。如果你更关心数值的绝对误差,即两个数值的差值是否在一定范围内,可以使用InDelta函数。如果你更关心数值的相对误差,即两个数值的相对差值是否在一定范围内,可以使用InEpsilon函数。

举个例子来说,如果你在进行浮点数计算时,希望比较两个数值是否在某个小范围内,可以使用InDelta函数。而如果你在进行科学实验或者统计分析时,希望比较两个数值的相对误差是否在某个小范围内,可以使用InEpsilon函数。

总之,选择使用哪个函数取决于你对数值比较的关注点是绝对误差还是相对误差。

英文:

From documentation:

https://godoc.org/github.com/stretchr/testify/assert#InDelta

> InDelta asserts that the two numerals are within delta of each other

https://godoc.org/github.com/stretchr/testify/assert#InEpsilon

> InEpsilon asserts that expected and actual have a relative error less than epsilon

And their code seems to be identical in purpose:

func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {

	af, aok := toFloat(expected)
	bf, bok := toFloat(actual)

	if !aok || !bok {
		return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...)
	}

	if math.IsNaN(af) {
		return Fail(t, fmt.Sprintf("Expected must not be NaN"), msgAndArgs...)
	}

	if math.IsNaN(bf) {
		return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...)
	}

	dt := af - bf
	if dt < -delta || dt > delta {
		return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...)
	}

	return true
}

func calcRelativeError(expected, actual interface{}) (float64, error) {
	af, aok := toFloat(expected)
	if !aok {
		return 0, fmt.Errorf("expected value %q cannot be converted to float", expected)
	}
	if af == 0 {
		return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error")
	}
	bf, bok := toFloat(actual)
	if !bok {
		return 0, fmt.Errorf("actual value %q cannot be converted to float", actual)
	}

	return math.Abs(af-bf) / math.Abs(af), nil
}

// InEpsilon asserts that expected and actual have a relative error less than epsilon
//
// Returns whether the assertion was successful (true) or not (false).
func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
	actualEpsilon, err := calcRelativeError(expected, actual)
	if err != nil {
		return Fail(t, err.Error(), msgAndArgs...)
	}
	if actualEpsilon > epsilon {
		return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+
			"        < %#v (actual)", epsilon, actualEpsilon), msgAndArgs...)
	}

	return true
}

What is the difference? what are the use cases where one would be used over the other and vice versa?

答案1

得分: 15

它们是相关但不完全相同的。

InDelta接收一个绝对值,并检查差异是否小于该值。

InEpsilon接收一个可接受的差异百分比。

InDelta的行为非常直观:

InDelta(t, 100, 101, 2) // 没问题
InDelta(t, 100, 103, 2) // 失败!

但有时候,你只关心实际值与期望值之间的差距不要太大。

根据期望值的大小,"不要太远"可能会对InDelta造成困扰。

对于任何数字使用相同的delta值可能会成为一个问题:

InDelta(t, 100, 101, 2) // 没问题,101与100的差距"不太远"
InDelta(t,   1,   2, 2) // 嗯,2与1的差距听起来"太远"了...

如果使用InEpsilon,你可以始终使用相同的百分比:

InEpsilon(t, 100, 101, 0.02) // 没问题,接受范围为102
InEpsilon(t,   1,   2, 0.02) // 不通过...这次接受范围为1.02

总之,InEpsilon的使用场景是丢弃小的差异(并将"小"相对于实际比较的值)。

英文:

They are related but not identical.

InDelta receives an absolute value and checks that the difference is less than that value.

InEpsilon receives a % of difference that would be acceptable.

The behaviour of InDelta is quite straightforward:

InDelta(t, 100, 101, 2) // that's OK
InDelta(t, 100, 103, 2) // fail!

But sometimes, you just care that the actual value is not too far away from the expected value.

Depending on how small or large the expected value is "not too far away" might get tricky with InDelta.

It might be a problem to use the same delta value for any number:

InDelta(t, 100, 101, 2) // ok, 101 is "not too far away" from 100
InDelta(t,   1,   2, 2) // hm, 2 sounds "too far away" from 1...

If you use InEpsilon, you can always use the same %:

InEpsilon(t, 100, 101, 0.02) // ok, up to 102 would be acceptable
InEpsilon(t,   1,   2, 0.02) // will not pass.. this time up to 1.02 would be acceptable

In summary, the use case for ÌnEpsilon is to discard small differences (and making "small" relative to the actual values compared).

huangapple
  • 本文由 发表于 2017年7月26日 01:46:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/45310329.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定