July 21, 2014
https://issues.dlang.org/show_bug.cgi?id=6725

--- Comment #17 from Vladimir Panteleev <thecybershadow@gmail.com> ---
(In reply to Jonathan M Davis from comment #16)
> Would you honestly expect folks to be using to!string or text to convert floating point values to Durations in their code on a regular basis? Sure, someone might do it, but it's an extra layer of ugly and a performance hit to do so. It doesn't _stop_ anyone from doing it, but it makes it hard enough and ugly enough that I'd expect most people not to do it.

Erm, yes, it is much less hard or ugly than your solution in comment #5.

--
July 21, 2014
https://issues.dlang.org/show_bug.cgi?id=6725

--- Comment #18 from Jonathan M Davis <jmdavisProg@gmx.com> ---
(In reply to Vladimir Panteleev from comment #17)
> (In reply to Jonathan M Davis from comment #16)
> > Would you honestly expect folks to be using to!string or text to convert floating point values to Durations in their code on a regular basis? Sure, someone might do it, but it's an extra layer of ugly and a performance hit to do so. It doesn't _stop_ anyone from doing it, but it makes it hard enough and ugly enough that I'd expect most people not to do it.
> 
> Erm, yes, it is much less hard or ugly than your solution in comment #5.

True,

auto d = dur!"seconds"(to!string(1.5001));

is not as ugly as

auto d = dur!"hnsecs"(cast(long)(convert!("seconds", "hnsecs")(1) * 1.5001)));

but it's definitely uglier than

auto d = dur!"seconds(1.5001);

and it incurs a performance hit when used in the cases when it shouldn't be used, so it makes it less desirable.

I feel very strongly (as does Walter apparently) that using floating point values for time calculations is very wrong and that we should not support it. If it's enough of a usability boost to support floating point values as strings in order to better deal with user input or duration literals for things like calls to sleep, then I'm willing to consider it, but even then, I'm not a huge fan of the idea. However, it's definitely more palatable than supporting floating point values directly, because that would just encourage bad practices. At least with strings, programmers would have to work at it a little bit and incur a performance hit when they used floating point values that weren't literals and weren't from user input. It's still not great, but allowing strings is a compromise, and from the sounds of it, it covers your use case.

--
July 21, 2014
https://issues.dlang.org/show_bug.cgi?id=6725

--- Comment #19 from Vladimir Panteleev <thecybershadow@gmail.com> ---
>  However, it's definitely more palatable than supporting floating point values
> directly, because that would just encourage bad practices.

Honestly, I think this is a poor argument. People who are in the mindset of doing *calculations* with floating-point values are likely going to do that anyway, using casts or using inferior precision (Walter stepped into this one already) or creating their own wrappers if they are so bent on it. Not accepting floating-point types for Duration conversions goes only a little way of stopping such misuse, and a long way towards annoying D users who have a valid use case for it, and forcing them to risk writing incorrect code because doing so correctly is not exactly trivial.

To reiterate, not all uses of Duration involve precise calculations. If the initial data comes from time measurements collected by the program, or ultimately the results are going to be used for timers or delays, minute errors will be negligible compared to computers' actual clock/timer resolutions.

I think that going as far as rejecting floating-point values because someone *might* mis-use them is an over-reaction. I think the problem can be placated by adding a reminder to the documentation that performing *calculations* with floating-point values can result in rounding errors.

--
July 21, 2014
https://issues.dlang.org/show_bug.cgi?id=6725

--- Comment #20 from Jonathan M Davis <jmdavisProg@gmx.com> ---
But honestly, is it really all that hard to just write a wrapper function?

auto floatDur(string units)(real value)
    if(is(typeof(convert!(units, "hnsecs")(1))))
{
    enum hnsecsPer = convert!(units, "hnsecs")(1);
    return hnsecs(cast(long)(value * hnsecsPer));
}

If you really want that, you can just put it in your own code without needing to make any adjustments to core.time and without making it so that we have to support (and therefore potentially encourage) the use of floating point values for time. And it avoids all of the ugliness in the previous examples. You just have to do floatDur(1.5001), and you have what you want except that the name of the function is slightly different, and you have to put that function in your own code somewhere.

I can understand wanting this functionality in core.time, but I really think that we shouldn't be supporting or encouraging the use of floating point values for time as it's generally bad practice. We can't stop anyone from doing it, as shown above, so anyone who wants to do it, can do it, but we don't have to directly support it either.

--
July 21, 2014
https://issues.dlang.org/show_bug.cgi?id=6725

--- Comment #21 from Vladimir Panteleev <thecybershadow@gmail.com> ---
> But honestly, is it really all that hard to just write a wrapper function?

The argument "can't you just put it in your code?" can be applied to many things in the standard library. I have needed the functionality several times in different programs, so considering that it is not trivial to do correctly, I think it should be in the standard library. Also, there is more than just `dur` - I think it should also be supported by the short-hand functions (`seconds` etc.), as well as `convert` and `Duration.total` (or some other way to get back a FP value from a Duration).

--
July 21, 2014
https://issues.dlang.org/show_bug.cgi?id=6725

--- Comment #22 from Steven Schveighoffer <schveiguy@yahoo.com> ---
I think adding the 'best' way to convert a floating point to core.time may be better than saying 'roll your own, and deal with floating point issues'.

Really, anyone can say: dur!"hnsecs"(val * (1000 * 1000 * 1000 / 100)), and
have a very bad conversion routine. This is not something we want people to do.

I'd rather have them do intervalToDuration(val), and do the best we can to avoid rounding errors. Once you are in Duration land, calculations are sane.

(yes, I'm suggesting just a single function that only supports interval in seconds instead of taking over functionality of dur)

(In reply to Jonathan M Davis from comment #16)

> Every application that I've seen use doubles for time has had bugs because of it.

I have a completely different experience, especially when the underlying core library supports it.

> It works in a lot of cases, but you end up with subtle problems as a result.

I think this is an exaggeration. It works in nearly all cases, since the least significant parts of a timestamp are not critical to get correct, and most timing code does not use equality for comparison.

> Durations aren't as bad as timestamps, but it's still asking for trouble.

This begs the question, what trouble? While I have experienced and seen issues with floating point, it has never been when checking if a timer has expired or figuring out how long to sleep for. Can you state some examples?

--
July 21, 2014
https://issues.dlang.org/show_bug.cgi?id=6725

--- Comment #23 from Jonathan M Davis <jmdavisProg@gmx.com> ---
(In reply to Steven Schveighoffer from comment #22)
> (In reply to Jonathan M Davis from comment #16)
> > Durations aren't as bad as timestamps, but it's still asking for trouble.
> 
> This begs the question, what trouble? While I have experienced and seen issues with floating point, it has never been when checking if a timer has expired or figuring out how long to sleep for. Can you state some examples?

In video-related code that I've had to deal with, we frequently need times to be exact (e.g. comparing a seek time against the start and end time of a clip). Using floating point values for that is an incredibly bad idea - one, because if you're foolish enough to compare for equality, then you're obviously going to have problems, and two, because the rounding error can make it so that one timestamp is now after another timestamp when it was supposed to be the same or slightly before (or the other way around, if you're looking at the beginning of a clip). The result is that if you use floating point values for timestamps like that, you very easily end up with floating point-related bugs.

Also, when attempting things like synchronizing multiple video streams, you're doing duration calculations to figure out what the timestamp is supposed to be, so if either the duration or the timestamp is a floating point value, then error is going to creep in. It's really not that uncommon in my experience to have to calculate timestamps or durations where those calculations will build up errors if floating point values are used.

Granted, I've worked in a fairly narrow field for a while now, so my experience may be different from other folks, but if you're doing anything related to timestamps or timing, using floating point values will result in bugs. And far too many folks I've worked with don't seem to realize that it's an issue, so they do it anyway. So, having it be harder to use floating point values with core.time is _good_ thing in my opinion.

--
July 21, 2014
https://issues.dlang.org/show_bug.cgi?id=6725

--- Comment #24 from Steven Schveighoffer <schveiguy@yahoo.com> ---
I think those concerns really don't apply here as much as you think. We are allowing the creation of durations via floating point numbers, not storing durations as floating point numbers, or doing math with floating point numbers.

(In reply to Jonathan M Davis from comment #23)
> 
> In video-related code that I've had to deal with, we frequently need times to be exact (e.g. comparing a seek time against the start and end time of a clip). Using floating point values for that is an incredibly bad idea - one, because if you're foolish enough to compare for equality, then you're obviously going to have problems, and two, because the rounding error can make it so that one timestamp is now after another timestamp when it was supposed to be the same or slightly before (or the other way around, if you're looking at the beginning of a clip). The result is that if you use floating point values for timestamps like that, you very easily end up with floating point-related bugs.

I think this situation would not arise. Code would still (I hope) be comparing SysTime's or Durations, not floating point values. One just uses a floating point value to generate a Duration.

If someone is going to use floating point values to store their main time stamps, then they are going to invite such problems, and of course the core.time types don't apply.

Remember, all the math is being done AFTER the conversion to integer-based Duration.

> Also, when attempting things like synchronizing multiple video streams, you're doing duration calculations to figure out what the timestamp is supposed to be, so if either the duration or the timestamp is a floating point value, then error is going to creep in. It's really not that uncommon in my experience to have to calculate timestamps or durations where those calculations will build up errors if floating point values are used.

I think the types we have already alleviate the need to work with floating point directly. Why would they use floating points for this instead of Duration/SysTime? Using floating point to specify literals, or to easily convert a user-supplied value to a duration doesn't mean one is going to just wholesale throw out all conveniences of std.datetime.

> Granted, I've worked in a fairly narrow field for a while now, so my experience may be different from other folks, but if you're doing anything related to timestamps or timing, using floating point values will result in bugs. And far too many folks I've worked with don't seem to realize that it's an issue, so they do it anyway. So, having it be harder to use floating point values with core.time is _good_ thing in my opinion.

For all of 2012, and 2013, I worked on a video project based on iOS and Linux, and on the iOS side, we used NSDateTime and NSTimeInterval, and really didn't ever have any problems. NSTimeInterval is typedef'd as a double. The experience level of the developers is probably different than yours, but I do believe quite a few people successfully use Apple's SDK for timing without issues.

I think we can put appropriate warnings on a function that does the conversion to allay concerns of abuse. If we make it *possible*, but not quite *comfortable*, I don't think it's such an issue. I.e. don't allow Duration += double, or SysTime + double.

Code like this is possible to get right and is accurate, I think we should support it to some degree:

auto timeout = Clock.currTime + durationFromInterval(1.2); // timeout in 1.2
seconds

--
July 23, 2014
https://issues.dlang.org/show_bug.cgi?id=6725

--- Comment #25 from Sobirari Muhomori <dfj1esp02@sneakemail.com> ---
(In reply to Vladimir Panteleev from comment #7)
> This argument is exaggerated. Not all measurements of time need to be precise. For example, if an utility program could accept a timeout value from the user, it's more useful to allow them to specify a fractional value.

That's an overengineered interface and should be done in your code. Fractions of timeouts are practically indiscernible, nobody would need to specify it that way.

(In reply to Steven Schveighoffer from comment #22)
> > It works in a lot of cases, but you end up with subtle problems as a result.
> 
> I think this is an exaggeration. It works in nearly all cases, since the least significant parts of a timestamp are not critical to get correct, and most timing code does not use equality for comparison.

We were unfortunate enough to depend on exact match of timestamps: an electronic signature algorithm dictates that signatures must be certified with a timestamp, so as the timestamp is hashed, it must match to the last bit and in our case it should work reproducibly across the following set of technologies: x86-32, x86-64, arm32, arm64, soft float, hard float, windows, linux, iOS, android, .net, java, obj-c, delphi, c++.

The examples presented are quite esoteric and don't prove necessity of having floatDur in standard library. Though, floatDur can be included in docs to help people copy it if they really need it.

--
July 23, 2014
https://issues.dlang.org/show_bug.cgi?id=6725

--- Comment #26 from Vladimir Panteleev <thecybershadow@gmail.com> ---
(In reply to Sobirari Muhomori from comment #25)
> That's an overengineered interface and should be done in your code. Fractions of timeouts are practically indiscernible, nobody would need to specify it that way.

Huh? That's pretty much the standard way to specify timeouts in many utilities (sleep, timeout, top...). I don't understand your argument - are you saying that no one should ever need to write a shell script that sleeps for less than 1 second?

(In reply to Sobirari Muhomori from comment #25)
> The examples presented are quite esoteric

IMHO the counter-examples presented here are the esoteric ones. Consider:

- hashing of time values
- a video editor which performs timestamp calculations using FP values

vs.

- allowing the user to specify a fractional number of seconds to sleep for - tweaking a literal in your code to sleep for 1.5 seconds instead of 1

--