我正在尝试编写测试以验证矩阵的逆,但是当我使用 FLT_EPSILON 来比较结果时,测试失败。
比较功能如下所示:test_assert_mat4_eq
我要做的是:
A = random matrix (4x4 float matrix)
B = inv(A)
C = inv(B)
assert(A == C) <---- fails
首先让我解释一下我如何计算矩阵逆; mat4是4x4浮点矩阵,如果启用SIMD,则矩阵的逆将通过SIMD指令(SSE2和AVX)计算。
如果SSE2已启用,您可以在glm_mat4_inv看到矩阵反码,然后通过glm_mat4_inv_sse2计算逆,同时还有glm_mat4_inv_precise_sse2版本,以避免 _mm_rcp_ps < / strong>指令。我使用第二个(glm_mat4_inv_precise_sse2)版本进行测试。
assert_true(fabsf(m1[i][j] - m2[i][j]) <= 0.0001);
传递我的macbook但它仍然在Linux上失败。
assert_true(fabsf(m1[i][j] - m2[i][j]) <= FLT_EPSILON);
这甚至不会传递给macos。也许与0.001相比也适用于linux,但精度太低。
我还在glm repo(https://github.com/g-truc/glm/issues/700)上创建了一个问题,因为这个问题对glm也有效。
这有什么问题?为什么精度太低?这个可以吗?我应该这样离开(通过删除测试或改变精度)?
注意:使用test_rand_mat4函数生成随机矩阵。但我只是用它来生成一个矩阵。我没有在任何地方使用任何随机矩阵,所有矩阵都是仿射变换矩阵,也许我应该使用仿射变换(这是主要目的)进行比较
答案 0 :(得分:3)
4⨉4矩阵足够小,您可以检测代码以打印每个计算步骤后涉及的每个浮点数,然后比较macOS和Linux结果以查看它们的不同之处。 (使用C %a
中的printf
格式打印十六进制浮点数或使用%.30g
之类的内容来打印十进制的整个值。)
告诉我们差异超过.0001是没有意义的,因为我们不知道你的数据是多少。通常,数字误差将与所涉及的一些数字的大小成比例。
如果在使用IEEE-754浮点的硬件上运行的macOS和Linux上执行“相同”操作,则应该实现相同的结果。因此,任何差异都可能是因为您没有使用相同的操作。这种差异的原因可能包括两个系统使用不同的源代码(例如,因为一个使用SIMD,因为SSE已启用,另一个使用标量代码)或编译器正在以不同方式编译代码。暂时覆盖SSE决策,以便在两个系统上测试相同的源代码。调试完成后,如果可能,在两个系统上测试SSE代码。之后,将SSE代码与非SSE代码进行比较。
您应该从简单的测试用例开始,而不是使用随机数据进行测试,并开始处理更多涉及的测试用例。从身份矩阵开始。简单的情况有助于调试基本逻辑,而不会涉及明显的浮点错误。然后修改元素以生成更复杂的案例。生成用于测试的矩阵时,请避免ill-conditioned matrices。我在生成用于测试矩阵逆的矩阵方面没有经验,因此您将不得不研究如何修改矩阵以改善其条件数,或者其他人可能会提出建议。
不要通过调用inv
两次来测试矩阵逆。这会错过简单的错误,例如复制和粘贴错误,导致名为inv
的例程实际上是矩阵副本或否定。相反,使用矩阵逆的已知良好参考实现或使用已知测试用例(单位矩阵的逆是单位矩阵,其他可以构造)或使用逆的其他属性(将逆乘以原始矩阵)应该产生单位矩阵)。使用double
作为测试代码。在4×4矩阵求逆中发现错误所需的测试用例数量不足以使性能变得重要。
答案 1 :(得分:0)
我仔细检查了测试代码,测试代码中出现了复制粘贴错误。矩阵反函数中使用 _mm_rcp_ps 指令来提高性能。问题是我希望在 _mm_rcp_ps 版本和非 _mm_rcp_ps 版本的测试中具有相同的精度。我已经修好了测试。
现在assert_true(fabsf(m1[i][j] - m2[i][j]) <= 0.0000009f);
在macOS和Linux上都传递了精确/准确的版本
和assert_true(fabsf(m1[i][j] - m2[i][j]) <= 0.0009f)
在macOS和Linux上都通过了快速( _mm_rcp_ps )版本。
相同的测试也传递给travis-ci。
另一方面,它不起作用,因为我使用了完全随机的矩阵,即使随机比例也不会因为病态矩阵而起作用(感谢@Eric Postpischil和@geza)