我正在艰难地完成“光线追踪器挑战”(Ray Tracer Challenge),并且在第4章中遇到了一个我无法超越的测试案例。
Scenario: Rotating a point around the x axis
Given p ← point(0, 1, 0)
And half_quarter ← rotation_x(π / 4)
And full_quarter ← rotation_x(π / 2)
Then half_quarter * p = point(0, √2/2, √2/2)
And full_quarter * p = point(0, 0, 1)
第一个断言(half_quarter)可以正常工作,但是第二个断言(full_quarter)失败。
预计Matrix.Multiply(full_quarter,p)等于(0,0,1,1),但找到(0,6.12323399573677E-17,1,1)。'
我对rotation_x
的实现如下:
public static double[,] rotation_x(double radians)
{
var cos = Math.Cos(radians);
var sin = Math.Sin(radians);
return new[,]
{
{ 1, 0, 0, 0 },
{ 0, cos, -sin, 0 },
{ 0, sin, cos, 0 },
{ 0, 0, 0, 1 }
});
}
这是我的乘法方法:
public static double[] Multiply(double[,] a, (double x, double y, double z, double w) b)
{
var product = new double[4];
for (var r = 0; r < 4; r++)
{
product[r] = a[r, 0] * b.x
+ a[r, 1] * b.y
+ a[r, 2] * b.z
+ a[r, 3] * b.w
;
}
return product;
}
在前面的章节中,我有很多关于矩阵和向量乘法的测试案例,因此,我很确定该部分可以正常工作。但是还有什么可能是错的?
答案 0 :(得分:6)
您正在传递π的某个值,但是Math.PI
并不完全等于π。大约等于π,在17个有效数字之内。
正好π的正弦正好为零。 非常接近π的正弦非常接近零。这就是您得到的:6 x 10 -17 几乎为零。
类似地,π/ 2的余弦值恰好为零,但是您传递的π/ 2的值仅精确到17位,因此结果仅精确到17位。
类似地,Sqrt(2.0)/2.0
也不完全是√2/ 2。大约精确到小数点后17位。
每个数字(不是整数)仅在17个小数位之内正确无误,因此这里没有奥秘。您的结果正确到小数点后17位,因为您的输入正确到小数点后17位。您只会得到与输入一样多的精度。双精度不是无限精度!
(顺便说一下,很好地使用元组类型-但考虑使结构代表这些概念中的某些;这样您就可以将与这些概念相关联的方法放入结构中。)