[m-rev.] time additions (was Re: for review: Add random.init/3)

Julien Fischer jfischer at opturion.com
Thu May 26 12:15:52 AEST 2016


On Thu, 26 May 2016, Paul Bone wrote:

> On Thu, May 26, 2016 at 11:25:16AM +1000, Peter Wang wrote:
>> On Wed, 25 May 2016 16:28:32 +1000, Peter Wang <novalazy at gmail.com> wrote:
>> 
>> A common way to seed the PRNG in C is
>>
>>     srand(time(0));
>> 
>> The time in seconds-since-the-Unix-epoch is useful for many other
>> purposes, in addition to seeding any PRNG implementation.
>> 
>> I would like to see an easy way to get an integer (simple)
>> representation of Unix time, as well as a way to convert between an
>> integer representation and the time_t values.
>> 
>> The main problem is probably that a signed 32-bit int cannot represent
>> Unix times past some time in 2038.  Apart from 32-bit machines, this
>> problem also affects C# and Java backends.
>> 
>> difftime/2 returns a float.  You can get the current unix time as a
>> float with
>>
>>     time(Now, !IO),
>>     Secs = difftime(Now, unix_epoch)
>> 
>> where `unix_epoch' returns a time_t.
>> 
>> Using floats is not very satisfying but in most cases they will be
>> double precision, and capable of representing any reasonable time
>> value precisely.

I think we should define Mercury's float type to always be a double
precision type and just do away with the spf grade component entirely.
That would deal with the "most cases" issue above.
(We don't currently give users the option of using single precision
values with the non-C backends, and if we did want to, adding a new
builtin type -- e.g. float32 -- to handle that case would be
preferable.)

>> Here are some suggested additions to time.m
>>
>>     :- func unix_epoch = time_t.
>>
>>     :- pred seconds_since_unix_epoch(float::out, io::di, io::uo) is det.
>>
>>     :- func seconds_since_unix_epoch(time_t) = float.
>>
>>     :- func from_seconds_since_unix_epoch(float) = time_t.
>> 
>> Thoughts?
>
> Eww,
>
> My reaction to the use of floats for this kind of thing is rater visceral.
>
> I agree that it may be useful to expose time in seconds since the epoch for
> other reasons.  And as an aside, gettimeofday() could be useful if portable
> too.
>
> I think there are a few potential solutions.
>
>  1 Use an int and hope for the best for 32-bit C / Java and C# platforms.

Can we do a little better than hoping for the best?

>    Maybe the world will end before 2038 and we'll also never need to store

Or assuming that world will end before 2038 ...

>    a date then or after, or if we do, just use 64-bit C.  While this isn't
>    a great solution I'm being partly serious, because I think it's better
>    than using floats.  Floats *shudder*.
>
>  2 Same as #1, but we also update the Java / C# backends to use 64bit
>    integers.  AFAIK these need to be boxed in many places anyway, so I
>    can't imagine this having any additional penalty.
>
>  3 Don't expose seconds since epoch, instead add a predicate to random.m
>    which takes a time_t and seeds the PRNG from that.  We could either
>    abusing our knowledge of the underlying representation of time_t or
>    create a hidden interface in time.m to expose something to read the
>    seconds-since-epoch.

4. Seed the PRNG using something other than time, for example:

    :- pred generate_random_int(int::out, io::di, io::uo) is det.

and generate the int using /dev/random (on Unix) or CryptGenRandom (on
Windows), or whatever the appropriate OS / platform specific source of
randomness is.  (Most OSs -- and certainly the ones that Mercury
actually runs on -- will provide something along those lines.)

Julien.


More information about the reviews mailing list