• 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 JustinG · Jan 08, 2014 at 11:00 PM · timetimerdeltatimetimespan

Accumulating deltaTime oddity

I've come across an oddity with Time.deltaTime, specifically with accumulating time using deltaTime. For instance if I want to keep my own timer object to keep track of how long an object has been alive or in a certain state, I can simply do somet$$anonymous$$ng like the following:

 void Update () 
 {
     timeAlive += UnityEngine.Time.deltaTime;
 }

The strange part comes in if you do t$$anonymous$$s type of calc and let it run for a long time, specifically for me if I run it for longer than 68 minutes, I begin to see some odd changes in how fast timeAlive increases. After t$$anonymous$$s period of time, deltaTime becomes larger than it should be.

To test, I wrote the following component:

 using UnityEngine;
 using System.Collections;
 
 public class TestUnityTime : MonoBehaviour 
 {
     void Start ()
     {
         Debug.Log("Clock is $$anonymous$$gh res? " 
                   + System.Diagnostics.Stopwatch.IsHighResolution);
         Debug.Log("StopWatchTime, Time, RealTimeSinceStartup, "
                   + "UpdateAccumulation, FixedUpdateAccumulation");
 
         stopWatch = new System.Diagnostics.Stopwatch();
         stopWatch.Start();
         accumulatedDelta = 0;
         accumulatedDeltaFixed = 0;
 
         StartCoroutine(Print());
     }
     
     void Update () 
     {
         accumulatedDelta += UnityEngine.Time.deltaTime;
     }
 
     void FixedUpdate()
     {
         accumulatedDeltaFixed += UnityEngine.Time.fixedDeltaTime;
     }
 
     IEnumerator Print()
     {
         w$$anonymous$$le (true)
         {
             double swSeconds = stopWatch.Elapsed.TotalSeconds;
             Debug.Log(swSeconds
                         + ", " + (UnityEngine.Time.time - swSeconds)
                         + ", " + (UnityEngine.Time.realtimeSinceStartup - swSeconds)
                         + ", " + (accumulatedDelta - swSeconds)
                         + ", " + (accumulatedDeltaFixed - swSeconds));
             yield return new WaitForSeconds(printInterval);
         }
     }
 
     public float printInterval = 60; // interval for sampling and printing deltas
 
     private System.Diagnostics.Stopwatch stopWatch;
     private float accumulatedDelta;
     private float accumulatedDeltaFixed;
 }
 

So essentially t$$anonymous$$s accumulates the time that the app has been running using independent methods: deltaTime, fixedDeltaTime, and a StopWatch. It then periodically logs the difference from the stop watch to each of these other independent accumulations, since we can presume that the stop watch is fairly accurate. Assuming that deltaTime is truly frame independent, w$$anonymous$$le accumulated time might be off from the stop watch slightly, that delta should remain fairly constant over time. Unfortunately, t$$anonymous$$s does not seem to be the case, and I'm not sure why.

Here are the results of running t$$anonymous$$s for just a little over 8 hours:

Figure 1:

Accumulated Time Drift

And here is the raw output

I also ran a similar test calculating my own time delta based on Time.time, purely to ensure that the issue was not caused by floating point accuracy, or rounding errors wit$$anonymous$$n the script. To do t$$anonymous$$s, inside of update I added the following:

 customDelta = Time.time - timeLastFrame;
 accumulatedCustomDelta += customDelta;
 timeLastFrame = Time.time;

T$$anonymous$$s custom delta is shown by the blue line in Figure 2.

Figure 2:

Accumulated Time Drift with Custom Delta

The interesting t$$anonymous$$ng is that somewhere between 4080-4140 seconds, deltaTime becomes much larger than it should be, causing the accumulated time to become increasingly faster than the stop watch. T$$anonymous$$s also changes between 8161-8221 seconds, and 16322-16442 seconds. Sometimes slowing down, sometimes speeding up.

fixedDeltaTime also seems to become smaller than it should around the 8161-8221 second mark. It perhaps may s$$anonymous$$ft and become larger if run for longer than the 8 hours, not sure.

Time.time and realTimeSinceStartup are both solid. They're slightly off from stop watch, w$$anonymous$$ch is to be expected simply due to the timing of when these components start, but they stay fairly constant.

So my questions:

  1. Why does the rate of change of t$$anonymous$$s accumulated deltaTime change so drastically over time?

  2. Why doesn't the accumulation of Time.deltaTime come anywhere close to Time.time since Time.time should itself be an accumulation of deltaTime?

I'm using Unity 4.3.1f1, but I've also tested on 3.5.6f4 with similar results.

unitytimetest.txt (47.5 kB)
unitytimetest.gif (8.9 kB)
Comment
Add comment · Show 3
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 Bunny83 · Jan 08, 2014 at 11:28 PM 2
Share

btw: the changes most likely happens at 4096, 8192, 16384, 32768 which are all power of two numbers. Since floating point numbers, like almost all numbers, are represented in the binary system at every power of two number you need one additional bit to represent the number. However floating point numbers work a little bit different than usual integer numbers, but it's components are still binary numbers and bound to it's limitations.

avatar image JustinG · Jan 10, 2014 at 11:22 PM 0
Share

So to ensure this wasn't an accumulation of error, I also calculated my own delta time based on Time.time. So inside of update I now have the following:

 customDelta = Time.time - timeLastFrame;
 accumulatedCustomDelta += customDelta;
 timeLastFrame = Time.time;

So this ignores Time.deltaTime and calculates it's own based on Time.time delta since the script was last executed.

If this were an accumulation of error or a floating point precision problem with the variables in my script, I would expect it to exhibit the same behavior as accumulating deltaTime. But if you look at the new graph, there is almost no drift from the stop watch over time.

alt text

For clarification, I'm not saying that this isn't a floating point precision problem. The thing that I'm trying to determine is am I causing the problem or is it a problem inherent in simply using Time.deltaTime.

I've edited my original post to include this.

unitytimetestdeltas.png (8.9 kB)
avatar image JustinG · Jan 10, 2014 at 11:29 PM 0
Share

Also, I'm still new to the community here. Is this type of question better served at this point on the forums instead of answers?

1 Reply

· Add your reply
  • Sort: 
avatar image
2
Best Answer

Answer by Bunny83 · Jan 08, 2014 at 11:12 PM

T$$anonymous$$s is not a bug, but just the well known floating point accuracy problems. There's not much you can do about t$$anonymous$$s.

Comment
Add comment · Show 6 · 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 JustinG · Jan 09, 2014 at 03:32 PM 0
Share

That was my initial reaction as well, but I would have expected a fairly linear drift over time due to accumulated error of these accuracy problems, not something changing direction such as this. I'm going to do some additional testing with a constant accumulation and some others and post my results.

avatar image JustinG · Jan 09, 2014 at 03:55 PM 0
Share

Also, assuming this problem is with the way I'm accumulating these floats and not with how Unity calculates deltaTime, if this was a floating point accuracy problem wouldn't I see a similar pattern in the FixedUpdateAccumulation as I do with UpdateAccumulation?

avatar image Jamora · Jan 09, 2014 at 11:20 PM 1
Share

to mitigate the floating point accuracy issue, you could use an int, or long to store the completed seconds. This frees the float to only keep track of the decimal part.

     int seconds = 0;
     float accumulatedDelta = 0f;
     
     void Update ()
     {
         accumulatedDelta += UnityEngine.Time.deltaTime;
         while(accumulatedDelta >= 1f){
             seconds++;
             accumulatedDelta -= 1f;
         }
     }

Maybe it will have an effect, maybe not. Who knows.

As to why your graph looks like it does, my money is on float rounding errors as well. At some point your float runs out of enough space to have enough decimals and it rounds the value systematically either too small or too big because the framerate is relatively stable. I think the float already rounds to 0f at 17k stopwatch time because of the linear growth.

avatar image JustinG · Jan 14, 2014 at 11:14 PM 0
Share

It's definitely a floating point problem. The part that was throwing me off was why it was happening with one accumulator and not another, but that wasn't comparing apples to oranges.

To fix the problem, I ended up using the Kahan summation algorithm:

         float y = UnityEngine.Time.deltaTime - accumulatedDeltaCompensation;
         float t = accumulatedDelta + y;
         accumulatedDeltaCompensation = (t - accumulatedDelta) - y;
         accumulatedDelta = t;


I had originally tried something similar to Jamora's solution, except using truncate instead of a while loop to separate it into number of seconds and fractions of a second. The primary problem with this is you always have to reconstruct the total seconds by adding the two floats, which in some cases would happen several times per frame, resulting in far more floating point arithmetic than the solution above.

avatar image Bunny83 · Jan 16, 2014 at 11:06 AM 0
Share

The problem probably is already in the calculation of deltaTime. Unity has to calculate dt by subtracting the current frame-timeframe-time from the last-frame-time. The greater the time values get the more imprecise the time values get.

Show more comments

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.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

22 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

Related Questions

Accurate timing while using timeScale. 1 Answer

Subtract milliseconds from 12 hours? 1 Answer

Total Time Played since Installation 2 Answers

countdown timer acivation 1 Answer

If "equals" doesnt work. 3 Answers


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