Bug 6725 – core.time.dur should accept floating point

Status
RESOLVED
Resolution
WONTFIX
Severity
enhancement
Priority
P2
Component
druntime
Product
D
Version
D2
Platform
Other
OS
Linux
Creation time
2011-09-24T14:15:00Z
Last change time
2014-07-24T16:19:36Z
Assigned to
nobody
Creator
siegelords_abode

Comments

Comment #0 by siegelords_abode — 2011-09-24T14:15:18Z
I'd prefer to be able to do this: dur!("secs")(1.0); dur!("secs")(1.5); dur!("secs")(1.5001); instead of having to do this: dur!("secs")(1); dur!("msecs")(1500); dur!("nsecs")(1500100);
Comment #1 by andrej.mitrovich — 2013-02-08T17:48:37Z
CC-ing jmdavis: Is it worth of adding this? This is your area of expertise. :)
Comment #2 by andrej.mitrovich — 2013-02-08T17:49:08Z
(In reply to comment #1) > worth of adding *worth adding
Comment #3 by issues.dlang — 2013-02-08T18:23:30Z
In general, using floating point values like that is asking for trouble, because you're going to run into precision problems. It's far better to use integral values with the more precise units, but I can see why someone would want to be able to do this. It wouldn't be all that big a deal to add it, and it's probably worth having. It's just of questionable value if you actually want to be precise.
Comment #4 by bugzilla — 2013-02-08T21:53:30Z
Mixing in floating point into what are properly integer operations is nothing but trouble.
Comment #5 by issues.dlang — 2013-02-08T22:26:54Z
Without dur or convert taking floating point values, you're forced to do something like auto d = dur!"hnsecs"(cast(long)(convert!("seconds", "hnsecs")(1) * 1.5001))); which is arguably overly verbose, but I really don't think that using floating point values for time should be encouraged at all. So, it could definitely be more user friendly, but I'm not sure that doing so would be a good idea. Too many people already seem willing to use floating point values for time (they did in the code base that I work on at work until I ported SysTime and Duration to C++ for work). So, I don't know what the right balance of user friendliness and good practice is in this case. TickDuration _does_ support some floating point stuff, but only converting _to_ them, not from them, and it's essentially a fraction internally. Though if I could go back, I probably would have gotten rid of TickDuration rather than integrating it into std.datetime, since I really don't think that it buys us much over Duration (hnsecs is already better precision than most system clocks anyway), and the fact that it's basically a fraction has caused quite a bit of trouble. It's particularly nasty when it comes to trying to test it.
Comment #6 by bugzilla — 2013-02-08T23:04:15Z
1. Using floating point for time is like using floating point for accounting software ($57.23). It doesn't work for things that come in discrete quanta (pennies, clock ticks). You will spend FAR more time chasing weird problems than you will save in "convenience". Yes, I have experience with this, and I understand floating point (having build FP emulators). 2. Many machines have very poor FP performance compared with integer performance. Implementing basic operations using unnecessary FP results in slow, bloated code. 3. Phobos' time functions are already massively complex. I still can't even compile the datetime unittests on a 512Mb machine. Please, no more. 4. It's trivial for a user to write their own FP wrapper around dur. There is no reason to integrate this into dur itself. Software should be pieced together using layers of abstraction, not kitchen sink base types. 5. It would take longer to read the documentation for this enhancement than to just convert your floating point value to an int. 6. You're not forced to write long complicated expressions to convert fp to integers. It's just a scale and a cast: dur!("secs")(1.5001) => dur!("nsecs")(cast(long)(1.5001 * 1000000)); I.e. I feel pretty strongly this is a bad idea for Phobos.
Comment #7 by dlang-bugzilla — 2014-07-21T11:47:45Z
Considering how difficult it is to convert an FP value to a Duration, I definitely think that this behavior belongs in the standard library. https://github.com/D-Programming-Language/druntime/pull/905 (In reply to Walter Bright from comment #6) > 1. Using floating point for time is like using floating point for accounting > software ($57.23). It doesn't work for things that come in discrete quanta > (pennies, clock ticks). You will spend FAR more time chasing weird problems > than you will save in "convenience". Yes, I have experience with this, and I > understand floating point (having build FP emulators). 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. > 2. Many machines have very poor FP performance compared with integer > performance. Implementing basic operations using unnecessary FP results in > slow, bloated code. FP code will only work when the user passes FP values to Druntime functions. It does not affect existing code. I don't see how this argument applies. > 3. Phobos' time functions are already massively complex. I still can't even > compile the datetime unittests on a 512Mb machine. Please, no more. "dur" is in Druntime (core.time), not Phobos. I have created a patch already, and it is rather simple. > 4. It's trivial for a user to write their own FP wrapper around dur. I disagree, it is not trivial. > There is no reason to integrate this into dur itself. I disagree, it is a useful addition that I wished a few times would be there. > Software should be pieced together using layers of abstraction, > not kitchen sink base types. I don't see how this applies here. Making the functions generic does add more abstraction. > 5. It would take longer to read the documentation for this enhancement than > to just convert your floating point value to an int. I believe that's objectively wrong. > 6. You're not forced to write long complicated expressions to convert fp to > integers. It's just a scale and a cast: > > dur!("secs")(1.5001) => dur!("nsecs")(cast(long)(1.5001 * 1000000)); I think this is a bad solution because: 1. It does not operate on the actual resolution of Duration, which is hnsecs. Forcing the user to move that up to their user code would imply a leaky abstraction. 2. Did he type 6 zeroes, or only 5? It's forcing the use of magic numbers. The non-magic-number expression has already been posted here (`convert!("seconds", "hnsecs")(1)`), but it makes the expression even bulkier. 3. It's duplicating logic which already exists in core.time. Opening up the existing conversion code to accept FP values was trivial - the existing algorithms takes care of FP values just fine. *** This issue has been marked as a duplicate of issue 13176 ***
Comment #8 by schveiguy — 2014-07-21T17:40:01Z
I feel like I should weigh in on this. While I agree it's not good to deal with floating point at a low level (Duration should never contain a floating point value), *creating* a duration based on a floating point is OK as long as the conversion is sound. Note that the most common usage of this would be for Duration literals. IIRC, you can guarantee the conversion is sound by using an epsilon. In other words, if you pass in 1.5, it's really 1 second, 500 milliseconds, not 1.4999999... seconds. I agree with Vladimir's point, even if there is some error in floating point representation, sleeping e.g. for 1.499 seconds vs. 1.5 isn't going to make a large difference. But I think we can largely avoid the floating point error. Where things become dicey is when you are accumulating lots of floating point values, or if you are working with very very small values. Note, the indicated mechanism to make a proper duration has improved somewhat: 1.second + 500.msecs + 100.usecs; I think this still looks awful. As a compromise, one could make a text-parser based mechanism, similar to octal: dur!"1.5001s"(); That translates into the above. Not sure how well this would turn out ;) A true "literal" syntax would make things easier to deal with.
Comment #9 by issues.dlang — 2014-07-21T18:24:14Z
(In reply to Steven Schveighoffer from comment #8) > Note, the indicated mechanism to make a proper duration has improved > somewhat: > > 1.second + 500.msecs + 100.usecs; > > I think this still looks awful. In most cases, the most that you'd need would be two pieces, not three. And I think that that's a small price to pay for avoiding floating point errors. > As a compromise, one could make a text-parser based mechanism, similar to > octal: > > dur!"1.5001s"(); > > That translates into the above. > Not sure how well this would turn out ;) > > A true "literal" syntax would make things easier to deal with. So, basically, you're suggesting that we create function or template which takes a string which represents a floating point value at compile time and generates a Duration from that, erroring out if it doesn't convert properly to hnsecs? That would be pretty easy to do (though I wouldn't reuse dur for that, since it already has a string template parameter - maybe durLit), and it would avoid the problem with floating point error. Any floating point string with no more than 7 decimal places (since that's what hnsecs will give you) and which wasn't too large for a long to hold once converted would convert just fine without floating point errors. That would certainly be better than allowing floating point values at runtime.
Comment #10 by schveiguy — 2014-07-21T18:56:37Z
(In reply to Jonathan M Davis from comment #9) > (In reply to Steven Schveighoffer from comment #8) > > Note, the indicated mechanism to make a proper duration has improved > > somewhat: > > > > 1.second + 500.msecs + 100.usecs; > > > > I think this still looks awful. > > In most cases, the most that you'd need would be two pieces, not three. And > I think that that's a small price to pay for avoiding floating point errors. > I was just going by the example you gave. You could do it in 2 pieces, but then you start getting into much larger numbers: 1.second + 500100.usecs; But I think there is a over-concern here about floating point conversion. It can be made to be exact, as long as you are dealing with literals. I remember running an extensive test back when I was working on the Tango time code. All you have to do is add an epsilon of .5 hnsecs or something like that.
Comment #11 by issues.dlang — 2014-07-21T19:12:54Z
(In reply to Steven Schveighoffer from comment #10) > I was just going by the example you gave. You could do it in 2 pieces, but > then you start getting into much larger numbers: > > 1.second + 500100.usecs; Yeah. I was just trying to point out that in practice, you pretty much would just need some number of seconds and the fractional seconds would then probably just be multiple of 10 of either msecs or usecs. Having to do something like 1.5001 is not likely to be common at all, so if it's a bit uglier, oh well. > But I think there is a over-concern here about floating point conversion. It > can be made to be exact, as long as you are dealing with literals. I > remember running an extensive test back when I was working on the Tango time > code. All you have to do is add an epsilon of .5 hnsecs or something like > that. I'm not opposed to something like durLit!("1.2552", "seconds") which is then essentially a Duration literal which can be a floating point value. However, using floating point values for time at runtime is just begging for bugs, and such code should be avoided with extreme prejudice.
Comment #12 by dlang-bugzilla — 2014-07-21T19:15:38Z
I'd just like to point out that there are more uses to FP time values than literals, such as user input. A template which accepts a string as a compile-time parameter solves none of my personal use-cases.
Comment #13 by issues.dlang — 2014-07-21T19:29:25Z
(In reply to Vladimir Panteleev from comment #12) > I'd just like to point out that there are more uses to FP time values than > literals, such as user input. A template which accepts a string as a > compile-time parameter solves none of my personal use-cases. If user input is the issue, a function which took a string which represented the time as a floating point and parsed it to a Duration would presumably do the trick while avoiding the floating point errors and without encouraging using floating point values for time in general. It would work well for user input (because it would already be a string) while being a royal pain for anyone who wanted to use floating point values for time directly, because they'd be forced to convert the floating point value to a string in order to then use the function to convert it to a Duration.
Comment #14 by dlang-bugzilla — 2014-07-21T19:32:23Z
> while being a royal pain for anyone who wanted to use floating point values > for time directly I think that's objectively false: just add `.text`
Comment #15 by schveiguy — 2014-07-21T19:35:19Z
(In reply to Jonathan M Davis from comment #11) > > I'm not opposed to something like durLit!("1.2552", "seconds") which is then > essentially a Duration literal which can be a floating point value. However, > using floating point values for time at runtime is just begging for bugs, > and such code should be avoided with extreme prejudice. I don't agree with that. It depends on the purpose of the duration value. Note that Objective-C uses Interval to represent all durations, and it is a typedef of double. I have not had any adverse experience with it. (In reply to Vladimir Panteleev from comment #12) > I'd just like to point out that there are more uses to FP time values than > literals, such as user input. A template which accepts a string as a > compile-time parameter solves none of my personal use-cases. Such a template can be built from a CTFE-able function, which would be available at runtime as well.
Comment #16 by issues.dlang — 2014-07-21T21:18:45Z
(In reply to Steven Schveighoffer from comment #15) > (In reply to Jonathan M Davis from comment #11) > > > > I'm not opposed to something like durLit!("1.2552", "seconds") which is then > > essentially a Duration literal which can be a floating point value. However, > > using floating point values for time at runtime is just begging for bugs, > > and such code should be avoided with extreme prejudice. > > I don't agree with that. It depends on the purpose of the duration value. > Note that Objective-C uses Interval to represent all durations, and it is a > typedef of double. I have not had any adverse experience with it. Every application that I've seen use doubles for time has had bugs because of it. It works in a lot of cases, but you end up with subtle problems as a result. Durations aren't as bad as timestamps, but it's still asking for trouble. It's just far better IMHO to avoid the floating point values entirely and not have to deal with those bugs, especially because using floating point values generally doesn't buy you much. (In reply to Vladimir Panteleev from comment #14) > > while being a royal pain for anyone who wanted to use floating point values > > for time directly > > I think that's objectively false: just add `.text` 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. With something like dur!"seconds"("1.5") or seconds("1.5"), the folks who want literals are fine, as is anyone getting user input, but we avoid directly supporting folks using floating point values for durations, and we avoid floating point errors with regards to literals or user input, whereas using actual floating point values risks floating point errors.
Comment #17 by dlang-bugzilla — 2014-07-21T21:23:44Z
(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.
Comment #18 by issues.dlang — 2014-07-21T21:42:02Z
(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.
Comment #19 by dlang-bugzilla — 2014-07-21T22:02:25Z
> 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.
Comment #20 by issues.dlang — 2014-07-21T22:04:23Z
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.
Comment #21 by dlang-bugzilla — 2014-07-21T22:09:26Z
> 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).
Comment #22 by schveiguy — 2014-07-21T22:15:52Z
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?
Comment #23 by issues.dlang — 2014-07-21T22:38:36Z
(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.
Comment #24 by schveiguy — 2014-07-21T23:42:31Z
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
Comment #25 by dfj1esp02 — 2014-07-23T06:48:56Z
(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.
Comment #26 by dlang-bugzilla — 2014-07-23T09:40:59Z
(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
Comment #27 by schveiguy — 2014-07-23T15:19:00Z
(In reply to Sobirari Muhomori from comment #25) > (In reply to Steven Schveighoffer from comment #22) > > > > 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++. That has nothing to do with allowing specification of durations via floating point. NOBODY is suggesting to replace the representation of a timestamp with a floating point value, just a mechanism to convert a floating point value into a duration (which would be represented, bit-for-bit, exactly the same on all those platforms. > 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. I agree with Vladimir. Specifying 1.5 or 1.2 seconds to sleep is not esoteric or even uncommon. Depending on exact hashing of timestamps across many different platforms is.
Comment #28 by issues.dlang — 2014-07-23T16:36:15Z
(In reply to Steven Schveighoffer from comment #24) > 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. Yeah. That reduces the problem considerably. Using floating point values for timestamps or durations is outright wrong IMHO, particularly when any calculations are done. Simply creating a duration from a floating point value isn't anywhere near as bad. But at the same time, it's pretty trivial to just do msecs(1500) instead of seconds(1.5).And many other libraries which deal with time such as Boost, the Java SDK, Joda, and Noda don't seem to accept floating points for constructing durations. I can definitely see why some folks would want it, but it goes against my every instinct about dealing with time stuff to allow floating point values. Maybe I just need to stew on this a bit more to convince myself that this isn't a horrible idea. Regardless, if we do it, it needs to add just enough functionality to give the usability boost that folks like Vladimir are looking for without adding general capabilities for handling floating point values (e.g. even if it's okay to make dur and its aliases support floating point values, I wouldn't want convert to, because that would then support more general floating point time operations).
Comment #29 by issues.dlang — 2014-07-23T16:38:07Z
I'm switching which of the two is marked as a duplicate, because this is the original, all of the discussion is here.
Comment #30 by issues.dlang — 2014-07-23T16:38:18Z
*** Issue 13176 has been marked as a duplicate of this issue. ***
Comment #31 by issues.dlang — 2014-07-23T18:49:53Z
Okay. I was going to say that allowing stuff like seconds(.033) would encourage a lack of precision even in cases where precision was required, and I really didn't lie that idea, but it looks like the lack of precision really isn't all that bad. The largest that the error gets is one hnsec: import std.algorithm; import std.datetime; import std.stdio; import std.string; void main() { long i = 0; immutable units = convert!("seconds", "hnsecs")(1); immutable mult = 1.0 / units; for(double d = 0; d < 1; d += mult, ++i) { auto result = cast(long)(d * units); assert(result == i || result == i - 1 || result == i + 1, format("%s %s %s", i, d, result)); } } So, simply constructing a Duration from a floating point value doesn't introduce much error. I still don't think that it's a good habit to get into, but with Duration and SysTime using long internally, we avoid any major problems. However, I don't think that we should be making convert work with floating point values like https://github.com/D-Programming-Language/druntime/pull/905 does, because that helps make the problem more general, and we should probably avoid making Duration.total support it unless someone has a really good reason for it, since it would make it easier to manipulate durations as floating point values, and that's not something that I think that we should support.
Comment #32 by dfj1esp02 — 2014-07-23T19:00:14Z
(In reply to Vladimir Panteleev from comment #26) > (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? It's meaningful to sleep for 200ms, but not for 0.2s. When you need a better precision, you switch to the appropriate unit. How would you specify 1/60 fraction of a minute? > (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 Digital signature is an important example. Cryptographic security is an important technology enjoying wide use. > vs. > > - allowing the user to specify a fractional number of seconds to sleep for Millisecond exists precisely for that purpose. In my experience millisecond precision works fine up to a scale of a minute (even though you don't need milliseconds for durations >2s). > - tweaking a literal in your code to sleep for 1.5 seconds instead of 1 It's again a need for a precision better than a second. Though, I'd still question that 1.5s is much better than 1 or 2 seconds. (In reply to Steven Schveighoffer from comment #27) > That has nothing to do with allowing specification of durations via floating > point. I replied to the statement that there's no problem with floats in time arithmetic. I'd say, it should be discouraged. > > 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. > > I agree with Vladimir. Specifying 1.5 or 1.2 seconds to sleep is not > esoteric or even uncommon. Depending on exact hashing of timestamps across > many different platforms is. The requirement arises from the fact that the signature is generated and checked on client, and clients are diverse in software and hardware. I presented a real range of technologies of our clients. Well, maybe I can withdraw the c++ requirement. BTW, the resolution was wontfix.
Comment #33 by yebblies — 2014-07-23T19:13:30Z
(In reply to Sobirari Muhomori from comment #32) > > BTW, the resolution was wontfix. Please do not close bugzilla issues that are still being discussed.
Comment #34 by dlang-bugzilla — 2014-07-23T19:49:47Z
(In reply to Sobirari Muhomori from comment #32) > It's meaningful to sleep for 200ms, but not for 0.2s. When you need a better > precision, you switch to the appropriate unit. How would you specify 1/60 > fraction of a minute? I still don't understand this argument. 200ms and 0.2s are the same thing, how can it be meaningful to sleep for 200ms but not for 0.2s? > Digital signature is an important example. Cryptographic security is an > important technology enjoying wide use. So are thousands and thousands of other technologies being in use on your computer right now. Anyway, I don't see how this applies anyway, since this particular proposed change does not concern itself with timestamps, or calculations of durations - only conversion. > Millisecond exists precisely for that purpose. In my experience millisecond > precision works fine up to a scale of a minute (even though you don't need > milliseconds for durations >2s). Are you saying that the program should just accept an integer number at the lowest precision it needs? That's just wrong: 1) it puts abstract technical reasoning before user experience; and 2) it strays from a well-established convention. > It's again a need for a precision better than a second. Though, I'd still > question that 1.5s is much better than 1 or 2 seconds. I don't understand this argument. Are you saying that no program should ever need to sleep for 1.5 seconds?
Comment #35 by bugzilla — 2014-07-24T01:41:51Z
(In reply to Vladimir Panteleev from comment #34) > 200ms and 0.2s are the same thing, > how can it be meaningful to sleep for 200ms but not for 0.2s? 0.2 cannot be represented exactly as a floating point value. Therefore, rounding error starts creeping in if you start adding many 0.2 increments. At the end, the books don't balance. Roundoff errors must be handled by the user, not the core library, because the core library cannot know what the user is doing with their FP calculations.
Comment #36 by dfj1esp02 — 2014-07-24T07:27:17Z
(In reply to Vladimir Panteleev from comment #34) > (In reply to Sobirari Muhomori from comment #32) > > It's meaningful to sleep for 200ms, but not for 0.2s. When you need a better > > precision, you switch to the appropriate unit. How would you specify 1/60 > > fraction of a minute? > > I still don't understand this argument. 200ms and 0.2s are the same thing, > how can it be meaningful to sleep for 200ms but not for 0.2s? Not quite the same. A second is an inadequate precision for specification of time with sub-second precision. Don't misunderstand me, I don't disallow you to use floating point for time specification, I only think it should not be encouraged for wide use, hence it should not be included in standard lib, but done in your code, which is trivial: floatDur is a very small function and can be included in docs to help people get it right with warning and discouraging at the same time, I think, it's an optimal choice given the tradeoffs and goals. BTW, just thought about another possible confusion: one can mistake 0.30h for 30 minutes. I use decimal time system at work, but it requires a considerable amount of time to get used to it (requires thinking about time in quanta of 6 minutes) and can be confusing when you see it for the first time. > > Digital signature is an important example. Cryptographic security is an > > important technology enjoying wide use. > > So are thousands and thousands of other technologies being in use on your > computer right now. That was a reply to your assertion that hashing of timestamps is an esoteric example. If the timestamp should be signed, you can't avoid it, if a duration should be specified with sub-second precision, you can use millisecond unit, if it should be specified with a week precision, you can use week unit, I don't see, how second can address all needs for duration specifications. Units+integers interface provided by standard library is superior to float interface. > > Millisecond exists precisely for that purpose. In my experience millisecond > > precision works fine up to a scale of a minute (even though you don't need > > milliseconds for durations >2s). > > Are you saying that the program should just accept an integer number at the > lowest precision it needs? That's just wrong: 1) it puts abstract technical > reasoning before user experience; and 2) it strays from a well-established > convention. Program should accept values in units of appropriate precision. I recommend 7zip as a example of good interface for specification of values in a wide range - from bytes to megabytes. I didn't check it, but believe it doesn't support float values, but frankly it doesn't need it. > > It's again a need for a precision better than a second. Though, I'd still > > question that 1.5s is much better than 1 or 2 seconds. > > I don't understand this argument. Are you saying that no program should ever > need to sleep for 1.5 seconds? Even if it does, specifying 1500ms is not an issue, but I'd still question its utility, I don't see how 25% difference can be practically notable.
Comment #37 by schveiguy — 2014-07-24T16:19:36Z
(In reply to Jonathan M Davis from comment #31) > Okay. I was going to say that allowing stuff like seconds(.033) would > encourage a lack of precision even in cases where precision was required, > and I really didn't lie that idea, but it looks like the lack of precision > really isn't all that bad. The largest that the error gets is one hnsec: > > import std.algorithm; > import std.datetime; > import std.stdio; > import std.string; > > void main() > { > long i = 0; > immutable units = convert!("seconds", "hnsecs")(1); > immutable mult = 1.0 / units; > for(double d = 0; d < 1; d += mult, ++i) > { > auto result = cast(long)(d * units); > assert(result == i || result == i - 1 || result == i + 1, > format("%s %s %s", i, d, result)); > } > } This is not a very good test, because mult as you defined it cannot be represented exactly in floating point. This means you are multiplying the error by quite a bit. A literal or parsed floating point value will not be so prone to error. (In reply to Walter Bright from comment #35) > (In reply to Vladimir Panteleev from comment #34) > > 200ms and 0.2s are the same thing, > > how can it be meaningful to sleep for 200ms but not for 0.2s? > > 0.2 cannot be represented exactly as a floating point value. Therefore, > rounding error starts creeping in if you start adding many 0.2 increments. > At the end, the books don't balance. This is if you do all your time calculations in FP. This doesn't make any sense, use Duration for your math. In other words: durFromInterval(someFPValue) * 100000 => no error, if we convert the FP value properly. durFromInterval(someFPValue * 100000) => Perhaps some error, but less stable than the above. > Roundoff errors must be handled by the user, not the core library, because > the core library cannot know what the user is doing with their FP > calculations. I disagree, we can add a small epsilon when converting. We know more than the user what the epsilon should be, since we define the discrete step (hnsec). Note, the user can ALREADY do this: dur!"nsecs"(cast(long)(someFPValue * 1_000_000_000)); We can't stop them from using FP. But let's not discount that this ISN'T the normal way to create a duration, it's a secondary option. And I'd much rather us define a mechanism to convert FP to duration than let the user deal with it.