Vegar
Vegar

Reputation: 12898

Matrix transformation, rotation around x axis

I'm struggling my way through The Ray Tracer Challenge and have come to a test case in chapter 4 that I'm not able to get past.

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)

The first assert (half_quarter) works as expected, but the second (full_quarter) fails.

Expected Matrix.Multiply(full_quarter, p) to be equal to (0, 0, 1, 1), but found (0, 6.12323399573677E-17, 1, 1).'

My implementation of rotation_x is as follows:

        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 }
            });
        }

Here's my multiplication method:

        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;
        }

I have a lot of tests cases for matrices and vector multiplication from earlier chapters, so I'm pretty sure that part works as it should. But what else could be wrong?

Upvotes: 1

Views: 357

Answers (1)

Eric Lippert
Eric Lippert

Reputation: 660463

You are passing in some value for π, but Math.PI is not exactly equal to π. It's approximately equal to π to within about 17 significant digits.

The sine of exactly π is exactly zero. The sine of something very close to π is very close to zero. Which is what you got: 6 x 10-17 is pretty close to zero.

Similarly, the cosine of π/2 is exactly zero, but the value of π/2 you are passing in is only accurate to 17 digits, so the result is only accurate to 17 digits.

Similarly, Sqrt(2.0)/2.0 is not exactly √2/2. It's an approximation accurate to about 17 decimal places.

Every number in your system that is not an integer is correct to only within 17 decimal places, so there's no mystery here; your results are correct to within 17 decimal places because your inputs are correct to within 17 decimal places. You only get out as much precision as you put in. Doubles are not infinite-precision!

(Nice use of tuple types, by the way -- but consider making structs to represent some of these concepts; that way you can put the methods associated with those concepts in the struct.)

Upvotes: 6

Related Questions