1

I'm pretty frustrated about this one ..

I have a timer called timer1 and a text box called TimeElapsedTextBox and a double variable called TimeTakenToFinish the timer ticks every 1 second (1000 millisecond) in the text box, I want it to display the time in this format:

Seconds.PartsOfSecond

Here is the Tick event:

private void timer1_Tick(object sender, EventArgs e)
{
  TimeTakenToFinish += (double)timer1.Interval / 10000;
  TimeElapsedTextBox.Text = TimeTakenToFinish;
}

it is actually displaying it in the text box the way i want it, but it's not counting properly .. I mean, it's counting less than a real second..

could you please tell me how to fix this ..

svick
  • 236,525
  • 50
  • 385
  • 514
vexe
  • 5,433
  • 12
  • 52
  • 81
  • Don't just add the requested interval on each tick. Rather, you should check to see how much time has actually elapsed, and add that number to `TimeTakenToFinish`. – dlev Jul 21 '12 at 00:04
  • If your only problem is that you need more precision, consider `System.Diagnostics.Stopwatch` class. It has a static property `IsHighResolution` whose `true` value indicates that your processor supports high resolution timers. See http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.ishighresolution.aspx for more details. – Jesse Hallam Jul 21 '12 at 00:06
  • 1
    You will want to use the .Elapsed (and any of the .ElapsedXXX properties) on a Stopwatch. The method you're currently using will, **each tick**, get some rounding error because the timer is **not guaranteed** to fire **exactly** at the rate you told it to. This will cause "your time" to drift off from reality each tick a bit more. Use the "delta" method (record `starttime`, then each tick display `starttime - currenttime`) to stay acurate. Or even better: use the [Stopwatch](http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx) class which was meant to do what you want. – RobIII Jul 21 '12 at 00:10
  • @Kivin I don't think Vexe needs high resolution anyways. A "regular" stopwatch would be fine for most things measured and displayed on a form. I don't think he's counting CPU cycles here ;) His problem is the method he's using (assuming the timer fires *exactly** every 1000ms which it doesn't). Even high resolution timers would, eventually, drift off using such a method. Windows (and most OS'es) isn't a RT-OS (and even those would drift eventually) – RobIII Jul 21 '12 at 00:18
  • @RobIII: A Stopwatch uses the system's high performance timer by default if available, so it's as high resolution as you're going to get out of the box. – Ed S. Jul 21 '12 at 00:22
  • 1
    @EdS. All fine and dandy but you don't **need** such high accuracy. So even if `IsHighResolution` would return false for some reason it would still be accurate enough to measure "CPS/WPM" as Vexe is trying to do. Assuming Vexe is now, with these answers, able to at least measure a minute without drifting a few ms each "tick" all you need to do is count keystrokes for a minute which will result in a CPS/WPM score +/- .01. Who cares if the measurement was 'accidentally' 00:01:00.015 ? That doesn't affect the score. If you were measuring F1 laptimes then you'd need a bit more accuracy. – RobIII Jul 21 '12 at 00:26

2 Answers2

8

Your problem here is a misunderstanding of the way your OS works. Sure, you can set the interval to 1000ms, but you cannot expect it to actually tick every second. You are running code on Windows, not a hard (or soft) real time operating system.

As an aside, you should also know that the resolution of your timer is finite, and as of today, limited to the accuracy of your system timer, which is probably about 15ms.

You cannot expect your code to perform that deterministically in that sort of environment. At any point the OS can preemptively kick you out of the CPU and start working on another task.

You simply cannot get the accuracy you desire, though I would ask; is it actually required? Probably not, but you haven't told us what you are actually trying to accomplish here, so who knows?

Also, this is wrong:

TimeTakenToFinish += (double)timer1.Interval / 10000;

Interval is a property which is used to tell the timer roughly how often it should fire the Tick event. You are not actually measuring anything, you may as well just be adding 1000.0 / 10000 to your counter every time.

If you need more precision use the StopWatch class which uses your CPU's high performance timer if available. You can still use a timer to periodically update the UI based on the current elapsed value of the Stopwatch, i.e.,

void timer1_Tick(...)
{
    var totalSeconds = _someStopwatch.ElapsedMilliseconds / 1000.0;
    TimeElapsedTextBox.Text = totalSeconds.ToString();
}
Ed S.
  • 122,712
  • 22
  • 185
  • 265
  • Actually, when i divided by 1000 like this: TimeTakenToFinish += (double)timer1.Interval / 1000; I got an accurate seconds in the text box. and to answer you Q, Yes, I need it to be accurate .. Why ? cuz i need that time to calculate the CPS and WPM for a typing program, the more accurate the time is, the more accurate the two are. – vexe Jul 21 '12 at 00:10
  • @VeXe: No. You *think* you are getting accurate results. In reality you are simply adding `1000.0 / 10000` to the counter every time the timer's Tick event is raised. The `Interval` property is not a record of how much time has elapsed between ticks, it is a static value that you set before you started the timer. You are not measuring anything. Use a StopWatch for accurate timing, that's what it's there for. – Ed S. Jul 21 '12 at 00:12
  • 1
    @VeXe "*Yes, I need it to be accurate .. Why ? cuz i need that time to calculate the CPS and WPM for a typing program, the more accurate the time is, the more accurate the two are*". Fine, but you need to put "accuracy" in perspective here. A resolution of 1 or even 10ms or 100ms would suffice for this purpose; *unless* your measurement is drifting (which yours does). Also, the longer the measured period is, the less accurate you need to be for 1 or 2 decimal digits CPS/WPM results. Think about it. – RobIII Jul 21 '12 at 00:24
0

Instead of using a timer, record the start time using DateTime.Now and then subtract the current time (DateTime.Now) from the start time. This will give you a more accurate timer as it uses the system clock instead which isn't affected so much by CPU performance.

Alternatively you can use System.Diagnostics.Stopwatch which does this for you.

You can still use an ordinary timer with an interval of less than a second to refresh the label displaying the time.

IntelOrca
  • 108
  • 2
  • 8
  • 1
    `DateTime.Now` has a resolution of ~15ms, which is about the same as the timer. If your code is sensitive to CPU performance I fail to see how calling `DateTime.Now` would be any better than using a timer. Your code isn't running, what does it matter which method you use? – Ed S. Jul 21 '12 at 00:05
  • 1
    @EdS. When you record the `starttime` and each "update" (e.g. tick) in your form you display the **delta** (e.g. `starttime - currenttime`) you will never be off by more than the ~15ms you're talking about. – RobIII Jul 21 '12 at 00:08
  • If your code isn't running to subtract current time from recorded DateTime.Now, you'll get no accuracy. The only way for what you suggested to work is to sit in a loop, using up as much CPU as you can comparing current time against a recorded time. Bad idea. – Peter Ritchie Jul 21 '12 at 00:09
  • @RobIII: Yes, that's true. I was speaking to the OP's intended goal of running some code every X milliseconds. – Ed S. Jul 21 '12 at 00:10
  • @Eds Ah, didn't know that. I assume the Stopwatch class uses a more accurate method. I think the point is that you should not be counting via a tick event. You should only display the elapsed time since the clock start time. – IntelOrca Jul 21 '12 at 00:11
  • Well, I think I misunderstood as well, because a Timer is a really weird way to time a piece of code or a process. I believe he is actually trying to simply time something, in which case he should use a Stopwatch as it uses the high performance timer if available. – Ed S. Jul 21 '12 at 00:13
  • @PeterRitchie Uh; ofcourse you will use a **regular timer** and in each tick (say, every ~1000ms) do the suggested substraction. Nobody suggested busy-loops here... – RobIII Jul 21 '12 at 00:14
  • Silly me, I gathered from the title that this was about "accurate...Timer) – Peter Ritchie Jul 21 '12 at 00:39
  • ThanX guys, I used the stopwatch, and it worked :) I left the text box displaying the ticks of the 1000 mills timer. and calculated the value of TimeElapsed from the stopwatch interval between the start and stop times which is indeed accurate .. :) – vexe Jul 21 '12 at 00:46