Skip to content

Vector3.Cross calculates wrong value #64375

@larsbjornfot

Description

@larsbjornfot

Using .net 6.0 and build release mode, 64-bit, in VS2022. The calculation of Vector3.Cross fails for no obvious reason. I also made my own Cross method (copy calculations from Vector3.Cross) and it too behaves strange.

I found the Cross result was affected by seemingly unrelated lines of code, and changing those unrelated lines makes it work.

In the Main method

  • a LINQ expression on a dummy variable
  • a dummy loop
  • passing vectors created from variables instead of constants

In my CrossAlt method:

  • WriteLine of the input vectors
  • WriteLine of temp crossAlt.Z
  • Test if crossAlt.Z is zero and throw exception

I have seen similar strange behaviour with Vector3.Dot and using .net5.0 and VS2019. Also 64-bit release mode builds. Then fixed it by “modifying unrelated lines until it worked”. Never seen it in debug mode.

This makes me think it’s a bug in the release-mode optimization for 64-bit code in .net 6.0 (and 5.0) or in the .net runtime.

(I left same bugreport in https://developercommunity.visualstudio.com/ a couple of weeks ago.)

namespace CrossBug
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Numerics;

    // Cross is calculated wrong. Sample output:
    // vector1 <1  0  0> vector2 <0  1  0>
    // cross <0  0  0>
    // crossAlt <0  0  0>
    //
    // Expected <0 0 1>
    // Change any of the places commented "Cross wrong" to get a correct value.

    internal class Program
    {
        public static void Main(string[] args)
        {
            Matrix4x4 indexToWorld = Matrix4x4.Identity;
            List<(short, short, short)> dummy = new() { (0, 0, 0) };

            // Cross wrong if keeping next. Comment out => cross ok.
            IEnumerable<float> radiusList = dummy.Select(
                c => Vector3.Transform(new Vector3(0, 0, 0), indexToWorld).Length());

            // Cross wrong if keeping dummy loop. Comment out => cross ok.
            for (int z = -1; z <= 1; z++)
            {
            }

            // Cross wrong if using indexToWorld. Change to constants => cross ok.
            var vector1 = new Vector3(indexToWorld.M11, indexToWorld.M12, indexToWorld.M13);
            var vector2 = new Vector3(indexToWorld.M21, indexToWorld.M22, indexToWorld.M23);

            // Cross ok with constants
            //var vector1 = new Vector3(1, 0, 0);
            //var vector2 = new Vector3(0, 1, 0);
            
            Console.WriteLine($"vector1 {vector1} vector2 {vector2}");
            var cross = Vector3.Cross(vector1, vector2);
            Console.WriteLine($"cross {cross}");

            var crossAlt = CrossAlt(vector1, vector2);
            Console.WriteLine($"crossAlt {crossAlt}");
        }

        public static Vector3 CrossAlt(Vector3 vector1, Vector3 vector2)
        {
            // Cross wrong if commented out. Uncomment WriteLine => cross ok.
            //Console.WriteLine($"vector1 {vector1} vector2 {vector2}");

            // Same calculation as Vector3.Cross
            var crossAlt = new Vector3(
                (vector1.Y * vector2.Z) - (vector1.Z * vector2.Y),
                (vector1.Z * vector2.X) - (vector1.X * vector2.Z),
                (vector1.X * vector2.Y) - (vector1.Y * vector2.X));

            // Cross wrong if comment out. Uncomment WriteLine => cross ok. (prints Z=1).
            //Console.WriteLine($"z {crossAlt.Z}");

            if (crossAlt.Z == 0)
            {
                // Cross wrong if comment out. Uncomment the throw => cross ok (so Z=1)
                //throw new Exception("Dummy");
            }

            return crossAlt;
        }
    }
}

Metadata

Metadata

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMIbug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions