go math/rand: 由于statsResults.maxError的误用,测试过于薄弱,

chhkpiq4  于 2个月前  发布在  Go
关注(0)|答案(3)|浏览(28)

数学/随机测试使用函数 nearEqual 来比较浮点数:

func nearEqual(a, b, closeEnough, maxError float64) bool {
	absDiff := math.Abs(a - b)
	if absDiff < closeEnough { // Necessary when one value is zero and one value is close to zero.
		return true
	}
	return absDiff/max(math.Abs(a), math.Abs(b)) < maxError
}

稍加思考,我们认为如果这个函数返回 false,maxError 应该总是很小。然而,maxError 通常在所有使用它的地方进行缩放(例如 stddev * 0.08),在实践中,它经常会变成 >> 2。
因此,许多数学/随机测试没有限制。一个极端的例子是 TestReadUniformity,它要求(除其他外)仅从两个随机选择的字节中获得的平均值必须非常接近 255.0/2,这是一个本应几乎总是失败的测试,但由于 maxError 的较大值,目前仍然成功。(相关: CL 51310 。)
将 maxError 替换为常量 0.08 导致大多数数学/随机测试通过,除了当 n 较小时调用 TestReadUniformity(此时它应该失败)。
然而,我没有足够的领域专业知识来判断这是否合理,或者是否有更好的替代界限。请求建议。golang.org/s/owners 列出了 math/rand 没有负责人,所以我会猜测并抄送:@rsc@aclements@robpike

xytpbqjk

xytpbqjk1#

https://golang.org/cl/51891提到了这个问题:math/rand: add Shuffle

nxagd54h

nxagd54h2#

这比我想象的还要糟糕。现有的代码如下:

func (this *statsResults) checkSimilarDistribution(expected *statsResults) error {
	if !nearEqual(this.mean, expected.mean, expected.closeEnough, expected.maxError) {
		s := fmt.Sprintf("mean %v != %v (allowed error %v, %v)", this.mean, expected.mean, expected.closeEnough, expected.maxError)
		fmt.Println(s)
		return errors.New(s)
	}
	if !nearEqual(this.stddev, expected.stddev, 0, expected.maxError) {
		s := fmt.Sprintf("stddev %v != %v (allowed error %v, %v)", this.stddev, expected.stddev, expected.closeEnough, expected.maxError)
		fmt.Println(s)
		return errors.New(s)
	}
	return nil
}

请注意,在检查标准差时,closeEnough在nearEqual调用中被设置为0。这意味着所有的工作都是由expected.maxError完成的,而它通常太大了。
此外,鉴于它们通常具有非常不同的尺度,mean和stddev应该分别有closeEnough和maxError的单独值。

ha5z0ras

ha5z0ras3#

https://golang.org/cl/55972提到了这个问题:math/rand: make Perm match Shuffle

相关问题