[futurebasic] Re: [FB] 64 bit to 32 bit math question

Message: < previous - next > : Reply : Subscribe : Cleanse
Home   : December 2007 : Group Archive : Group : All Groups

From: Robert Purves <listrp@...>
Date: Mon, 3 Dec 2007 13:25:31 +1300
Walter Lenk wrote:

> I have for some time now been using a milliSecond timing routine  
> derived from the Mac's microSecond clock in many of my projects, and  
> have recently been wondering about some of the 64 bit to 32 bit math  
> involved. The process is fairly simple (complete code follows):
>
> 1. The current uS time is read into 'TimeuS'
> 2a. IF this is the first call after the program starts (gStartTimeuS  
> == 0),
>     THEN 'TimeuS' is stored in 'gStartTimeuS' and 0 is returned.
> 2b, ELSE 'gStartTimeuS' is subtracted from 'TimeuS' to get the uS  
> time from
>     program start. I do this because one of my clients leaves his  
> machines
>     running 24 hours a day, and the unsigned long output will only  
> hold
>     24.9 days worth of mS before it goes negative.
> 3. Turn 'TimeuS' into the long 'mSecs&' by dividing by 1000 -> this  
> is what I'm not sure of.


A 32 bit ms counter will overflow (i.e. start again from zero) after  
49.7 days. You need to maintain more bits. fn a_mSeconds# below  will  
"never" overflow. Note that fn a_mSeconds# returns a double; don't  
assign this to a integer type variable, or you'll be in overflow  
territory again.

Robert P.

'--------------------------
include "Tlbx Timer.incl"

toolbox U64Subtract( UInt64 * out, UInt32 inLeftHi, UInt32 inLeftLo,  
UInt32 inRightHi, UInt32 inRightLo )

// UInt64out = UInt64a - UInt64b
local fn U64Subtract( @out as ^UInt64, @a as ^UInt64, @b as ^UInt64 )
'~'1
U64Subtract( #out, a.hi, a.lo, b.hi, b.lo )
end fn

local fn DoubleFromUInt64#( @u as ^UInt64 )
'~'1
dim as double  tmp
tmp = u.lo
if ( u.lo < 0 ) then tmp += 4294967296.0
//if ( u.hi > 2097152 ) or ( u.hi < 0 ) then stop "Loss of precision  
in DoubleFromUInt64#"
end fn = 4294967296.0*u.hi + tmp


/*
  ms since first call, as double value
*/
local fn a_mSeconds#
'~'1
dim as UnsignedWide rawMicroseconds, diffMicroseconds
begin globals
dim as UnsignedWide sStartMicroseconds
end globals

Microseconds( @rawMicroseconds )
long if ( sStartMicroseconds.lo == 0 and sStartMicroseconds.hi == 0 )
sStartMicroseconds = rawMicroseconds
end if
fn U64Subtract( @diffMicroseconds, @rawMicroseconds,  
@sStartMicroseconds )
// convert diffMicroseconds to ms as integer-valued double
end fn = fix( fn DoubleFromUInt64#( diffMicroseconds ) * 0.001 )



// demo main
print fn a_mSeconds#
print fn a_mSeconds#
print fn a_mSeconds#
delay 1000
print fn a_mSeconds#
stop
'--------------------------