Page 1 of 1

ElapsedMilliseconds() gets negative

Posted: Wed May 21, 2014 10:02 pm
by c4s
Introduced in Windows 8, turning your computer off will actually put it into some kind of hibernation mode. That's great because it'll help to shut down and boot much faster!

Anyway, if you've read the title you know that this is not why I started this thread. Instead I wanted to let you know what I just found out: ElapsedMilliseconds() isn't very reliable anymore, because it will not be reset when turning you computer off - instead only when restarting it.
Technically this might even be correct behavior. However on the user side it's kind of strange to see statistics (e.g. the Task Manager) telling me that my computer is on for 20 days although I just turned it on a couple of hours ago.

The problem: Since PureBasic is only returning a signed long variable for ElapsedMilliseconds() things can get weird if the computer is "on" for about 24 days. Here and there I'm using a little code to check if a certain delay has passed:

Code: Select all

If ElapsedMilliseconds() > LastTime + 1000  ; At least 1 sec passed?
    LastTime = ElapsedMilliseconds()
    ; [...]
Starting with Windows 8 chances are way higher that this code will fail because after 24 days ElapsedMilliseconds() will return a negative value...

Obviously it would be great if ElapsedMilliseconds() could just return a quad value (which will be safe for the next 290 million years ;)). But apart from that are there any quick fixes for my problem?

Re: ElapsedMilliseconds() gets negative

Posted: Wed May 21, 2014 10:20 pm
by freak
If you use the x64 version then the result is 64bit and there is no problem.

Also, it is only problematic on the exact rollover time. As soon as both your start and end measurement are negative, there is no problem anymore because the difference between the two will again be a positive value.

Re: ElapsedMilliseconds() gets negative

Posted: Thu May 22, 2014 12:53 am
by Demivec
c4s wrote:But apart from that are there any quick fixes for my problem?

Code: Select all

If ElapsedMilliseconds() > LastTime + 1000   Or ElapsedMilliseconds() < LastTime; At least 1 sec passed or clock rollover?
    LastTime = ElapsedMilliseconds()
    ; [...]

Re: ElapsedMilliseconds() gets negative

Posted: Thu May 22, 2014 1:23 am
by Tenaja
freak wrote:Also, it is only problematic on the exact rollover time. As soon as both your start and end measurement are negative, there is no problem anymore because the difference between the two will again be a positive value.
If your start time is anywhere in the first 21 days, and the check-time is anywhere after that, then you have hit that exact rollover time. Obviously there is a workaround to deal with it.

Probably a relatively simple, and very safe solution would be to convert the 32-bit value to 64-bit, then do the math (convert back if necessary). Is there a bitwise copy that does not sign-extend? I'm sure I have seen some macros on here somewhere.

Re: ElapsedMilliseconds() gets negative

Posted: Thu May 22, 2014 6:06 am
by Danilo
Long Overflow:

Code: Select all

ms.l = 2147483640

For i = 0 To 20
    Debug ms    
    ms + 1
Next
Convert to quad as unsigned number:

Code: Select all

ms.l = 2147483640

For i = 0 To 20
    ; convert to quad
    quad.q = ms & $FFFFFFFF
    Debug quad
    
    ms + 1
Next
The value returns to 0 after 4294967295 (because we are reading a long), which is 49 days.
You still need to check for new ElapsedMilliseconds() values not being smaller than the last value.
If the new value is smaller, do the math for the overflow.


64bit ElapsedMilliseconds for 32bit systems:

Code: Select all

CompilerIf #PB_Compiler_Processor = #PB_Processor_x86

    Procedure.q ElapsedMilliseconds_64()
        Static ElapsedMilliseconds_64_oldValue.q = 0                ; value of last call
        Static ElapsedMilliseconds_64_overflow.q = 0                ; how many overflows occured
        Protected current_ms.q = ElapsedMilliseconds() & $FFFFFFFF  ; get new value as unsigned number
        If ElapsedMilliseconds_64_oldValue > current_ms             ; If old value is greater than new value
            ElapsedMilliseconds_64_overflow + 1                     ;     increment overflow by 1
        EndIf
        ElapsedMilliseconds_64_oldValue = current_ms
        ProcedureReturn current_ms + ElapsedMilliseconds_64_overflow * $FFFFFFFF ; return current value + overflows
    EndProcedure

    Macro ElapsedMilliseconds()
        ElapsedMilliseconds_64()
    EndMacro
    
CompilerEndIf

For i = 0 To 20
    ms.q = ElapsedMilliseconds()
    Debug ms
    Delay(20)
Next
- Must be used with quad variables (.q).
- Because of signed Quads in PB, this overflows after 106751991167 days (round about 292471208 years).

Re: ElapsedMilliseconds() gets negative

Posted: Thu May 22, 2014 6:09 pm
by c4s
Thanks to all of you for the great suggestions!