Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 14 Next capture
2021 2022 2023
2 captures
12 Jun 22 - 14 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by fherbst · Aug 27, 2015 at 10:19 AM · c#quaternionfloating point

Quaternion.Angle is inaccurate

I have a strange issue with comparing angles. I do want to detect if an object rotated during the frame, using

 if(Round(lastRotation, transform.rotation)) { // rotations are equal }
 
 bool Round(Quaternion a, Quaternion b) {
     return Quaternion.Angle(a, b) < 0.001f;
 }

However, that returns false for some quaternions that are absolutely equal. Here's a debug print of those:

 //  (first value: Quaternion.Angle(a,b); -- this should be zero! Then the Quaternions xyzw; then the eulerAngles xyz)

 diff_rot: 0.03956468; (0.02575354000000000000, 0.96469100000000000000, -0.24099940000000000000, 0.10308820000000000000) != (0.02575354000000000000, 0.96469100000000000000, -0.24099940000000000000, 0.10308820000000000000); (28.05311000000000000000, 167.80090000000000000000, 0.00000024185890000000) != (28.05311000000000000000, 167.80090000000000000000, 0.00000024185890000000)

Why on earth do two exactly equal rotations (even equal in floating point!) return a non-zero angle? And not even a small one, but 0.03 degrees, which would be a visible change in rotation?

Here's a script to test yourself this strange behaviour: link text

quaternionbug.zip (726 B)
Comment
Add comment · Show 15
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image pako · Aug 27, 2015 at 11:06 AM 0
Share

I'm a bit confused here... You say:

returns true for some quaternions that are absolutely equal

I can't see why it should return false... returning true seems correct: since the rotations are equal, the angle between them will be zero (or a very-very small float), which will be smaller than 0.001f (your condition), hence true.

You also say:

Why on earth do two exactly equal angles (even equal in floating point!) return a non-zero angle?

Which exactly is the non-zero angle you are referring to? Your printout only shows rotations in world space expressed as quaternions and Euler angles.

avatar image fherbst · Aug 27, 2015 at 11:50 AM 0
Share

Corrected the mixup. Of course I mean that it returns false... Also, I added a script to test it yourself.

avatar image Scribe · Aug 27, 2015 at 12:35 PM 0
Share

Seems like it might be an unfortunate outcome of floating point errors, you can use the following to avoid it, however its a bit messy:

 float Round(Quaternion a, Quaternion b) {
     float aL = $$anonymous$$athf.Sqrt(a.x*a.x + a.y*a.y + a.z*a.z + a.w*a.w);
     float bL = $$anonymous$$athf.Sqrt(b.x*b.x + b.y*b.y + b.z*b.z + b.w*b.w);
 
     a.x /= aL;
     a.y /= aL;
     a.z /= aL;
     a.w /= aL;
 
     b.x /= bL;
     b.y /= bL;
     b.z /= bL;
     b.w /= bL;
 
     return Quaternion.Angle(a, b) < 0.001f;
 }

avatar image Maxii Scribe · Apr 01, 2016 at 06:17 PM 0
Share

@scribe Did this Quaternion.Angle issue ever get resolved and is there a bug to vote on? I couldn't find one.

Your code above normalizes each Quat if I read it correctly (I'm not very Quat knowledgable). Why would that fix the problem, and have you confirmed that the problem comes from assigning transform.rotation rather than Angle?

avatar image Bunny83 Maxii · Apr 02, 2016 at 12:00 AM 0
Share

I don't think there'a a bug report on it yet but feel free to create one ^^. The problem is actually simple floating point limitation. The quaternion value in question is normalized but the floating point error is large enough to produce an error of about 0.03 degree.

Unity's Angle method simply uses the dot product and Acos like that:

 public static float Angle(Quaternion a, Quaternion b)
 {
     float f = Quaternion.Dot(a, b);
     return Mathf.Acos(Mathf.Min(Mathf.Abs(f), 1f)) * 2f * 57.29578f;
 }
 
 public static float Dot(Quaternion a, Quaternion b)
 {
     return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
 }

The values in question simply have reached their limit of accuracy:

 (0.0257535400, 0.9646910000, -0.2409994000, 0.1030882000)

The dot product of the quaternion itself will yield a value of:

 0.9999998580

Acos of that value converted to degree will yield the angle 0.0305338

That's a generally known problem due to the floating point limitation. Renormalizing the quaternions might help a little bit, but some values might still give you some problems.

See this SO question which is about the same algorithm. The answers to the question mention an alternative way to calculate the angle between two quaternions. It's a bit more complex to calculate but should be more accurate.

Show more comments
avatar image pako · Aug 27, 2015 at 12:42 PM 0
Share

O$$anonymous$$, I run the script and found the source of the error by single stepping through code in the debugger. However, I'm not sure how to correct it.

It's not the Quaternion.Angle function being inaccurate, i.e. if you run the following it will return an angle of 0:

     void Start()
     {
         Debug.Log(Quaternion.Angle(transform.rotation, transform.rotation));
     }

The error arises when q is assigned to transform.rotation:

         Quaternion q = new Quaternion(0.02575354000000000000f, 0.96469100000000000000f, -0.24099940000000000000f, 0.10308820000000000000f);
         transform.rotation = q;


i.e. after the assignment the following values are in stored in transform.rotation:

x: 0.0257535446f -- 46 added at the end

y: 0.9646911f -- 1 added at the end

z: -0.24099943f -- 3 added at the end

w: 0.103088215f -- 15 added at the end

I believe this shouldn't happen, so it seems to be a bug.

Anyway, whenever you compare the assigned value to the original value, they are not equal because of this.

No idea how to correct it or work around it...

avatar image fherbst · Aug 27, 2015 at 01:43 PM 1
Share

@Scribe, quaternions returned by Unity should always be normalized already, so normalizing them does not change anything.

@pako, I already reported a bug to Unity. Also, more investigation showed that the specific returned value seems to be the lowest non-zero value ever reported by Quaternion.Angle, so it indeed seems to be floating point-related. However, since that is the lowest value reported, I'll just go with changing the allowed angular difference to 0.04f.

So the curious point remains that this returned angle should be, compared to floating operations, very big (0° 2"), but at least I have a fix.

Show more comments

1 Reply

· Add your reply
  • Sort: 
avatar image
-1

Answer by Amir1360 · Jan 14 at 02:35 PM

It seems that Quaternion.Angle function is not accurate. Try the following function instead:

 float AcurateAngle(Quaternion a, Quaternion b)
 {
      GameObject go = new GameObject();
      go.transform.rotation = a;
      Quaternion.Inverse(b).ToAngleAxis(out float angle, out Vector3 axis);
      go.transform.Rotate(axis, angle);
      go.transform.rotation.ToAngleAxis(out angle, out _);
      Destroy(go);
      return angle;
 }

 


Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Pangamini · Jan 14 at 03:09 PM 1
Share

Wow, creating and destroying a gameObject just to calculate an angle seems brutally inefficient

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

32 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Distribute terrain in zones 3 Answers

Multiple Cars not working 1 Answer

Calculating the rotation of an object, when the top is forward 1 Answer

(c#) NPC rotator 1 Answer

How make child object rotate inverse to its parents rotation ? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges