Sign in to follow this  

An Interesting Scenario with Windows Timers

This topic is 3845 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Today, A small and not much noticable problem in my program has occured. After a short time of trying to solve it, I was so close to giving up, but eventually it led me to reveal A very interesting scenario/problem (in my opinion). Perhaps some of you are familiar with it. I appologize for the long story but I believe it is not much complicated and it also has almost no source code that you will have to decipher :) I do have a few question in the end that are based on what I've wrote. It all started as a weird problem in my Direct3D program: The rotation of a model was quite unsmooth although the FPS was definitely high enough. I've made lots of rotations in various programs in the past with the same code and no such problem has ever occured. After some investigation I've noticed that the statement '((float) timeGetTime () / 1000.0f)' has changed each 5 frames or so, even though the timeGetTime () returned different values each frame. I guess that at this point most of you have already figured out why this is happening. However, I was completely confused and and wrote a simple test program. Here is the first code I wrote:
	DWORD times [100], tm;//list of times, temporary time
	for (int i = 0; i < 100; i++) {
		tm = (DWORD) timeGetTime ();
		if (i != 0 && tm == times [i - 1]) {
			i--;
			continue;
		}
		times [i] = tm;
	}










All this does is simply filling the 'times' array with 100 unique closest as possible times returned by timeGetTime (). This code (the 'for' loop) ran for an unnoticable amount of time (actually 100 ms) and the values in the array were x,x+1,x+2,x+3, ... ,x+99. So far so good. Now I've changed the 'times' and 'tm' variables from DWORD to double (and changed the casting to double as well). Everything was the same this time. But now I've changed them to floats. I was completely surprised: It took about 6 seconds to complete the loop! Accordingly, the difference between the numbers in the 'times' array was about 60 (ms). Ok so again, most of you probably know why this is happening, and so do I after investigating it for too long. Here is my explanation (might be inaccurate, so please correct me): the 'float' type was designed (or just turned out) so that it would be able to hold more accuracy after the floating point, but less possibility for big numbers, such as those which DWORD can hold. Since both types are 32 bit (in 32 bit systems) and DWORD can contain values as high as 4GB, float wouldn't be able to get to that number since it needs to use many bits for the fractional data (I am aware that it isn't just a simple seperation of the 32 bits into 2 unrelated groups, but it is irrelevant here). This inaccuracy causes close big numbers that are converted to floats to turn out completely the same (same 32 bit data) and thus indistinguishable. When my program ran, it actually hasn't found any difference (inside the 'if') between the 2 high number floats till the point where the real difference between them was about 60. Now this explains exactly what happend in my 3D rotation program: These 60 miliseconds are approximately the 5 equal rotation frames, due to the fact that each frame is called every 12 ms (83 FPS). <Please hold on for a little longer, I'm almost done (few questions in the end)> There is still one thing that is a bit mysterious in all this story: Why has this weird scenario never happened to me till now, and why (I assume so) is it so rare? For those of you who haven't guessed yet, the answer is extremely simple: My computer has been running for almost two weeks now! no restarts too of course. timeGetTime () retrieves the system time in miliseconds, so 12 days for instance is about 1GB of miliseconds (1/4 of the maximum value for a DWORD), and this is with no doubt high enough to cause a float to go crazy and lose its accuracy:
	float f1, f2;
	f1 = (float) 1000000090;
	f2 = (float) 1000000035;
	f1 == f2;//true










By the way, the system time is wrapped around each 49.71 days (4GB of miliseconds) for the same reason, and I wouldn't dare to miss that moment in my system. I fully expect, fear and wish for something similar to the millennium bug to occur on my system (I know it didn't ever happen, but still, isn't it exciting?)! I just beg for no power failures; Waiting another 50 days is wasting almost 1% of my entire life!!! I would like to conclude by saying this (I believe its quite important): When your computer is running for a long time, some inaccuracies might occur, which can be noticable in 3D programs for example, and when developing programs that could be affected by this, you should not ignore this case and dedicate another 5 minutes to solve it. A simple solution is to first retrieve the system time into a DWORD and to check whether its a really big number, and if it is then just subtract a large number from it and then you may convert it to a float. I made a simple test and the results show that at about 134M (thats only one and a half days of the system running without reboots), a float's data will change each 10 units, so timing problems might start to occur a bit after that. When I think about this a little deeper, the chances of this problem to happen are actually probably very low due to the specific circumstances that can make it happen, but even so, I believe that this is an interesting scenario and I hope you liked reading my analysis of it. I would like to thank every one that has read my 'story' or parts of it. Now a few questions: 1) About the small 'time test program' that I've wrote: I tried to make it more comfortable by making a template function that would receive the type of the variable to test the loop with. However, no matter what I did, it somehow always took these 6 seconds to finish the loop, as if I told it each time to use the type 'float'. Any ideas about this? 2) The float type problem (6 seconds) doesn't happen when I build the program in release mode. Now, I can understand that release mode optimizes a lot, but making a float more accurate? what's going on? 3) Although the release mode did solve the problem in the test program, it didn't make the 3D program rotate the model more smoothly. I'm a bit confused here... 4) I've once saw somewhere about an option to use double precision instead of floats in Direct3D programs. Is this true? How to specify that? I would extremely appreciate any answers, corrections, comments, maybe impressions too, or anything else you have to say. I'm truly sorry for the long post, I know I got carried away a bit but I tried to fully review what happend. And again, Thank you very much for reading this, Uri. [Edited by - UriKiller on June 3, 2007 8:53:10 AM]

Share this post


Link to post
Share on other sites
The problem is as you've said - the float type can holder larger and smaller numbers than integers and can hold fractions, but it can't represent every number in it's range. IEEE floats use 23 bits for the value, doubles use 52 bits, and long doubles use 64 bits. Those values are basically equal to significant figures, but in binary. A float can thus represent every integer from -2^23+1 to +2^23-1 (8388607) but past that digits will be lost.

The proper solution is to store times as DWORDs (or whatever type the functions return naturally, such as QWORD for QueryPerformanceCounter) and only use float (or double for QWORD) for the difference between times.

In other words, something like
DWORD Time1, Time2;
float DeltaTime;
//...
DeltaTime = float(Time2-Time1) / 1000.00f;


[Edited by - Extrarius on June 3, 2007 1:20:46 PM]

Share this post


Link to post
Share on other sites

This topic is 3845 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this