These Numerical Inconsistencies Are Driving Me Nuts

Brace yourselves, I have a few questions but I will try to be as clear as I can. There is a TL;DR at the bottom.

Ever since I have started using Unity I feel like I am always wrestling with inaccurate calculations, especially whenever I am dealing with movement. My latest endeavors require that I calculate the angular velocity of my character controller so naturally I had a look around the internet to see if someone had already invented the wheel. I happened upon this thread which ended up being pretty useful.

Anyways, the code given in the 18th post (by BenZed) ended up worked pretty well but as he notes in his post, ā€œit does seem to fluctuate somewhatā€. I ended up tweaking it slightly to try and remove these fluctuations and the following is what I ended up with.

using UnityEngine;
using System.Collections;

public class ConstantSpin : MonoBehaviour
{
    RotationTracker rotationTracker;

    private Quaternion lastRotation;
    private Quaternion newRotation;

    void Start()
    {
        rotationTracker = GetComponent<RotationTracker>();

        lastRotation = transform.rotation;
    }

    void Update()
    {
        newRotation = Quaternion.Euler(new Vector3(90.0f, 90.0f, 0.0f) * Time.deltaTime) * lastRotation;
        transform.rotation = newRotation;

        lastRotation = newRotation;
    }

    void LateUpdate()
    {
        Debug.Log(rotationTracker.angularVelocity.magnitude);
    }
}
using UnityEngine;

public class RotationTracker : MonoBehaviour
{
    //Holds the previous frames rotation
    Quaternion lastRotation;

    //References to the relevent axis angle variables
    float magnitude;
    Vector3 axis;

    public Vector3 angularVelocity
    {
        get
        {
            //DIVDED by Time.deltaTime to give you the degrees of rotation per axis per second
            return (axis * magnitude) / Time.deltaTime;
        }
    }

    void Start()
    {
        lastRotation = transform.rotation;
    }

    void LateUpdate()
    {
        Quaternion deltaRotation = transform.rotation * Quaternion.Inverse(lastRotation);
        deltaRotation.ToAngleAxis(out magnitude, out axis);

        lastRotation = transform.rotation;
    }
}

So these two scripts are attached to a cube in the scene and that’s it. The first script rotates the cube by a constant vector of (90, 0, 90) every second. The second script attempts to record the change in rotation (deltaRotation) and has a read only property called angularVelocity that returns just that.

The main thing I did was move the rotation tracking logic and the debug log call to LateUpdate(), the reasoning being that slight differences in when the two scripts update may be causing the fluctuation in the reported angular velocity. This did seem to fix most of the wildly large fluctuations recorded, but the small ones remained.

One of the most annoying fluctuations is this large one that always happens in the third debug log.

Here are the magnitudes from message 1:

And here are the vector results from message 1:

From my calculations the magnitude results are pretty close, the vector (90, 0, 90) should have a length of about 127.2792, however, the printout of the vector itself seems rather strange to me. I expected both x and z to be 90 (or close to it), but I expected y to be 0 (although this may be due to a lack of understanding in vector math on my part, please correct me if I am wrong). Lastly, the y value seems to crash down to a much lower value in log three which correlates with the sudden drop in vector magnitude in log three in the first image. This is strange because the other two values seem to drop very little comparatively.

Luckily the fluctuations seem to be very small and steady after the first couple frames, and I could live with the fact that the first couple frames of playing in the editor are wacky, but it leaves me wondering why there are even spikes like this at all if the code does what I think it does (and Time.deltaTime doesn’t lie). If the spike is frame-rate dependent, then will my game’s code malfunction later on mid-play? I hate seeing data like this because I know not addressing it now will bite me in the ass down the road when there are many more variables at play.

Also I stripped out all the pieces of code I could and even when both uses of Time.deltaTime are removed the code still spits out fluctuating values (try small values like vector like (5, 0, 5) as the code seems to behave erratically when the cube is spinning too fast).

TL;DR

I guess what I’m really asking is: are inaccuracies in time-based values (e.g. movement or angular velocity) just something that game developers live with (like floating point error), or should I continue to assume that something is wrong with the code? It really bothers me that a calculation this basic would fluctuate at such low levels of precision, but if this kind of fluctuation is to be expected then it will put my mind at rest and I will be able to focus on coding to account for it instead of feverishly spending hours trying to debug it.

To anyone who read all of this I thank you,
~PartyBoat

I suspect that this is probably a result of some floating point number being slightly off than the expected value. I’d check out these two things first.

  1. In the editor make sure your script execution order is correct. It looks like your LateUpdate with the print statement in it depends on happening after the RotationTracker. If its happening before you could be using an old rotation value with the current delta time which could result in a little bit of error in the estimated angular velocity.

  2. Rather than using Update you might want to think about using FixedUpdate and Time.fixedDeltaTime. Update and Time.deltaTime are called basically as fast as the engine can handle and because of this the value is allowed to fluctuate around. Fixed time will remain constant and will not be dependent on computer performance. This can result in much more accurate physics calculations.

Beyond that if there will always be some amount of floating point error. It will generally be smaller than is see-able by the human eye though and for the use of games that is generally acceptable.

Hope this helps you!

Thanks for the reply bpeake!

I just tried both things you mentioned (together and separate) and unfortunately neither one has seemed to have any effect. The closest I got was putting everything into fixed update and then removing all operations involving Time.fixedDeltaTime. For some reason this seemed to yield perfectly accurate results, but the object rotated way too fast. Shouldn’t the code in FixedUpdate() only run a maximum of whatever I set it to per second? The really weird part is that I scaled the rotation down by 60 (I had a hunch that number would work because my fixed timestep is set to 60 updates per second), and I scaled the angularVelocity tracker up by 60 to compensate and the errors returned! Without any references to Time.fixedDeltaTime or Time.deltaTime in the code at all! (Maybe it’s an error in FixedUpdate()?)

So I still haven’t completely given up on this because the most accurately I can measure angular velocity so far is to one decimal place, anything more precise and the number starts to show fluctuations. Honestly I think this just isn’t acceptable. I could see dealing with floating point error at around the 5th or even 4rd decimal place but seeing fluctuations in the second decimal place seems ridiculous to me. I only shudder to think if I will ever need to calculate something that must be perfectly accurate. Luckily as you said, I can probably get away with inaccuracies this time.

Thanks again!

You’ll not get a constant rotation per second, due to floating point errors. Let’s go through the number of float-based calculations you’re doing:

//Finding the rotation:
newRotation = Quaternion.Euler(newVector3(90.0f, 90.0f, 0.0f)*Time.deltaTime)* lastRotation;

//broken down:
Vector3 euler = new Vector3(90f, 90f, 0) * Time.deltaTime; //A floating point multiplication
Vector3 rotation = Quaternion.Euler(euler); //A floating point conversion
newRotation = rotation * lastRotation; //A floating point multiplication
    
//Calculating the magnitude and axis:
Quaternion deltaRotation= transform.rotation*Quaternion.Inverse(lastRotation); //One multiplication and one conversion
deltaRotation.ToAngleAxis(outmagnitude, outaxis); //Who knows, but at least two operations

//Finally the calculation:
public Vector3 angularVelocity
{
    get
    {
        return (axis * magnitude) / Time.deltaTime; //One floating point multiplication, one floating point division
    }
}

Each of the float operations will have a tiny little error, unless it’s possible to express it float perfectly with the exponential equation that makes up a float. There’s at least 9 operations happening here, and each of the vector and quaternion operations contains at least 3 or 4 float multiplications, respectively. I’m not very clear on the inner workings of these structs, or how accurate floats are, but to me it looks like you’ve got in the ballpark of 30+ float operation, each which contributes it’s own error to the total. Those errors build up!

What exactly are you trying to do where the accuracy of calculating the exact rotation of an object is important? There’s ways to do things that mitigates these kinds of errors, but what method to use is dependent on the use case.

It’s not that I’m doing something that explicitly requires this kind of accuracy, it’s that I’m concerned that such basic calculations are this inaccurate at all. When I eventually do need to calculate a value accurately that involves many floating point operations then should I just assume that I will get similar results (which would be unacceptable in that case)?

No I don’t really buy that this is just the byproduct of using a lot of floating point operations. Many applications in computing require extreme precision with several compounding floating point operations and somehow they don’t run into the same issue (NASA or a CNC machine couldn’t function with calculations this inaccurate). I’d also like to point out that I can compute one frame’s worth of calculations by hand on my computer’s calculator and the results are extremely accurate and don’t fluctuate. There is no way that the Windows calculator takes more processing power or is significantly different in execution than the operations I am using in my code, yet my code is significantly less accurate.

Anyways, I do appreciate the input Baste, I just have a hard time believing it.

Fixed time is not constant and does depend on computer performance; that’s what the ā€œmaximum allowed timestepā€ setting is for in the time settings (physics will go slower if the CPU is stressed too much). Unity will attempt to keep it constant but there are no guarantees by any means. FixedUpdate should not be used for anything except physics, since it fires every physics tick. Manually moving objects should be done in Update/LateUpdate, using Time.deltaTime. In any case I imagine the discrepancies are the result of quaternion/euler angle conversions.

–Eric

Hey Eric, thanks for the clarification.

You mentioned that the cause of this problem might be related to my quaternion to euler angle conversions. Is there any way to completely avoid making these conversions? I mean only using euler angles would be possible but I’d rather take advantage of the benefits of quaternions. Also doesn’t Unity use quaternions under the hood? So technically aren’t euler angles always converted to quaternions at some point?

Also I don’t think working purely in quaternions is even possible, you have to feed it an angle at some point right?

Yes.

No, you can use quaternions directly (Unity does, after all), as long as you understand how they work.

–Eric

Hey, finally someone has written a decent use case for Trend.Me. And they told me nobody would be interested. :wink:

You have a complicated multi step process. At least one step, and possibly several, is introducing noise to your process. At this stage you have no idea which step is the problem. Everyone is guessing which step. Its like the performance threads all over again where the correct answer is ā€˜use a profiler’. I’m picking there is one step where you have made a mistake, and are multiplying instead of dividing, or something similar.

So what you want to do is isolate which step in your data flow is producing noise. In order to do this you need to track the output of each separate step. Something like Trend.Me can do this. Or you can write your own trender using animation curves.

Once you have the data you can check the amount of noise in each step. There are statistical ways to do this, but for a data manipulation process simply eye balling the trends should be enough. Start from the first step and check each one in turn. Once you find the step where the noise is introduced then you have your step to fix. And its almost certainly a logic/mathematics error.

To emphasise: This is probably not floating point precision error. Floating point errors occur when you use large numbers and small numbers together, or when you do millions of consecutive calculations and the errors can accumulate. If this was just floating point error you would see the seemingly random fluctuations way down the digit chain.

1 Like

Hey BoredMormon,

Your post made me remember that I have purchased squiggle, which has been very useful for this kind of debugging in the past (and is essentially the same as Trend.Me). So I worked through my code step by step to find the culprit and after iterating on this several times here is what the code currently looks like:

using UnityEngine;
using System.Collections;

public class SpinAndTrackQ : MonoBehaviour
{
    private Quaternion lastRotation;
    private Quaternion newRotation;
    private Vector3 angularVelocity;

    //References to the relevent axis angle variables
    private float magnitude;
    private Vector3 axis;
    private Quaternion lastTrackedRotation;

    void Awake()
    {
        lastRotation = transform.rotation;
        lastTrackedRotation = lastRotation;
    }

    void Update()
    {
        DebugGraph.Log("lastTrackedRotation_before", lastTrackedRotation.eulerAngles.magnitude);

        newRotation = Quaternion.Euler(new Vector3(0.0f, 90.0f, 0.0f) * Time.deltaTime) * lastRotation;
        transform.rotation = newRotation;
        lastRotation = newRotation;

        // Find the difference between the starting rotation "lastTrackedRotation"
        // to the ending rotation "transform.rotation" (Quaternion subtraction).
        Quaternion deltaRotation = Quaternion.Inverse(lastTrackedRotation) * lastRotation;
        deltaRotation.ToAngleAxis(out magnitude, out axis);

        lastTrackedRotation = transform.rotation;

        angularVelocity = (axis * magnitude) / Time.deltaTime;

        DebugGraph.Log("lastTrackedRotation_after", lastTrackedRotation.eulerAngles.magnitude);
    }
}

As you can see I have made two calls to ā€œDebugGraph.Logā€ which in this case produced these two graphs:

The graph on top is the DebugGraph.Log() call made at the very beginning of Update() and the second graph is the call made at the very end.

You might have noticed something strange here and that is while the state of my variable ā€œlastTrackedRotationā€ looks clean and and is not fluctuating at the end of the Update() loop, suddenly interference has been introduce by the time we get back to Update() again, without any changes to lastTrackedRotation being made!

How is this even possible!?

I feel like tearing my hair out at this point. Maybe I just don’t see something obvious because I’ve been trying to debug this problem for several hours straight, but at this point it feels like the gremlins crawling around inside Unity are messing with my variables just to spite me.

To my knowledge the only thing that could be causing this is something internal in Unity or possibly a bug in Squiggle (the debug graphing asset), although I haven’t had problems with Squiggle in the past so honestly I trust it more than Unity’s black box.

P.S. The script I posted is the only thing attached to the object. I don’t even have any other scripts running on anything else in the scene.

This is another thread of finding the splat of bad internals. And its good. I wish all Uniters would be able to get to these kinds of threads first. But alas tis only aft of the headaches and searches.
I got here trying to find a revolution counter. I have spent weeks wading through rotation explanations ad nauseum because people mix rotations with revolutions. From behind my headache I for the life of have too many other examples or maybe the answer is hidden in the coding somewhere. I am not sure at this point.
What I do know is my blindness inhibits me from seeing the clear answers.
If some has further answers I would appreciate it.