November 22, 2017
On Wednesday, 22 November 2017 at 05:50:53 UTC, Walter Bright wrote:
> There is another, perhaps obsolete, consideration.
>
> Some CPUs do not have floating point units. C, for example, is carefully set up so that when you don't need floating point, it isn't required to have an FPU. This made C usable on cheap processors.
>
> It's sorta like "why is the GC linked in even though I never used it?"

Hi Walter - I wonder if there is a miscommunication. My understanding is that the question is whether there should be a built-in conversion from Duration to float/double in a specific unit of measure, like milliseconds. It sounds as if your concern is more to ensure that the time package not be required to support something other than integral values in its internal operations.

Perhaps there is an alternative perspective, but being able to convert a Duration to a double in a specific unit of measure would not seem to place any burden on the time package to support the result of conversion as a first class time object. In this view, what happens with the converted value is entirely in the hands of the end user, not the time package. If the user decides to use the double in a mathematical operation, floating point round off error is the responsibility of the user, not the time package.

To give a concrete example of why this is conversion is useful - I often do performance analysis involving service calls that take from single digit milliseconds to hundreds of milliseconds, with standard deviations in that ballpark. I might use tens of thousands of timing samples and analyze those samples using stats and graphing packages. Those packages all use doubles, and it is generally convenient to work in units appropriate for the measurements being done, milliseconds in the case I described. In this case, the last step in generating a timing value is to convert to milliseconds as a double and store it somewhere, often a log file. These will be read back in by the stats/graphing package, where follow-up processing will be done.

In the older version of StopWatch, this last step conversion could be done with a call like:

    double ms = sw.peek.to!("msecs", double));

In the new version, a call needs to be something like:

    double ms = sw.peek.total!("hnsecs").to!double / 1e+04);

The latter form is more error prone and less clear about intent, etc. It sounds as the rationale for depreciating the previous form of conversion is because the end user may incur floating point round-off error by performing mathematical operations on the double value. The user can still perform the conversion, but must go to greater effort. It sounds as if the other part of the rationale is that conversion is likely to be rare. In my experience, this is not the case.
November 22, 2017
On 22.11.2017 03:22, Walter Bright wrote:
> On 11/21/2017 1:40 PM, Timon Gehr wrote:
>>> Computer clocks have discrete ticks, they are not continuous.
>> That may be true, but the plotting library may still just expect a list of doubles. What's the point of removing the simple conversion function that was already available for such use cases? This is a breaking change with zero benefits.
> 
> I'm in general opposed to "kitchen sink" abstractions, preferring pluggable component abstractions.

But this is orthogonal to my complaint. I don't care which Phobos module contains the conversion functionality. It can be part of std.conv.to, for example.

> Floating point has no business being in a type that is represented as an integral type.

Why would it need to be part of the type? I just want the obvious conversion functionality, to enable further processing where this is appropriate.

> There are no 0.3 clock ticks, the notion does not exist.
> ...

(Great then. There also is no 0.3 float value. :P)

> 
>>> The behavior maps cleanly onto integral math,
>> I'm not doing computations on times. I could just use Duration for that.
> 
> Time durations are always discrete quanta by the nature of the clock used.
> ...

The plotter or whatever other component consumes the timing data might not care about this.

> 
>>> not fuzzy fp math.
>> There is nothing fuzzy about floating point operations,
> 
> Fuzzy as in inexact.

The result is well-defined, it's just rounded. Whether that is exact or not depends on what you expected the result to be, it's not properly part of the floating point abstraction.

> Integral time computations are always an exact multiple of clock ticks.
> ...

The reason why floating point values are popular is that it is often enough infeasible or unnecessary to do the computation without rounding. The output of a computation might be inexact even if the inputs are not. There needs to be some way to move from one regime to the other.

> 
>> but still, yes, for some use cases, the tiny rounding error will just not matter.
> 
> Whether the rounding error matters or not should not be decided by the time package, it should be decided by what the user decides to do with the time values.
> 
> I.e. it is not properly part of the time abstraction.
> 

My claim is not that conversion from time to floating point values associated with a few natural units is "properly part of the time abstraction", just that it should exist. Do you agree with that?
November 22, 2017
On 22.11.2017 06:50, Walter Bright wrote:
> There is another, perhaps obsolete, consideration.
> 
> Some CPUs do not have floating point units. C, for example, is carefully set up so that when you don't need floating point, it isn't required to have an FPU. This made C usable on cheap processors.
> 
> It's sorta like "why is the GC linked in even though I never used it?"

Why would the conversion function be linked in if I never use it?
November 22, 2017
On 11/21/2017 11:48 PM, Jon Degenhardt wrote:
> Hi Walter - I wonder if there is a miscommunication. My understanding is that the question is whether there should be a built-in conversion from Duration to float/double in a specific unit of measure, like milliseconds. It sounds as if your concern is more to ensure that the time package not be required to support something other than integral values in its internal operations.

My perspective is that the time package should not deal with floating point at all. I understand that it is useful in some circumstances to treat them as floating point values, but this should be a layer added on by the user, not by the time package.


> In the older version of StopWatch, this last step conversion could be done with a call like:
> 
>      double ms = sw.peek.to!("msecs", double));

That puts the logic of the double conversion into the time package.


> In the new version, a call needs to be something like:
> 
>      double ms = sw.peek.total!("hnsecs").to!double / 1e+04);
> 
> The latter form is more error prone and less clear about intent, etc.

I agree. It is a prime candidate for further encapsulating in a function, such as:

    /* Returns: duration in milliseconds to 4 digits past the decimal point */
    double swAsDouble(StopWatch sw) { return sw.peek.total!("hnsecs").to!double / 1e+04); }

    double ms = swAsDouble(sw);

This function, being a trivial one liner, doesn't really need to be in Phobos. It could be suitable for inclusion in the time package documentation as an example on how to get results in a floating point manner.

[As a general philosophy, I oppose Phobos being filled with one liners, a mile wide and an inch deep. Phobos should be a small number of non-obvious, orthogonal, deep functions. As an extreme example,
   int add2(int i){return i+2;}
should not be in Phobos.]


> It sounds as the rationale for depreciating the previous form of conversion is because the end user may incur floating point round-off error by performing mathematical operations on the double value. The user can still perform the conversion, but must go to greater effort. It sounds as if the other part of the rationale is that conversion is likely to be rare. In my experience, this is not the case.

It is not the rationale I would use for this case.

It's also true that floating point operations are a minefield with respect to precision and roundoff decisions. These sorts of decisions should not even be made by the time package, because they will always be wrong for some users, so they should rightfully be pushed to the user.
November 22, 2017
On 11/22/2017 1:41 AM, Timon Gehr wrote:
> Why would the conversion function be linked in if I never use it?

Good question. It depends on how the code is written, and how the compiler represents it in the object file, and how the linker deals with unreferenced parts of object files.

`format`, for example, dynamically decides whether to use floating point or not, so it is always linked in.
November 22, 2017
On 11/22/2017 1:38 AM, Timon Gehr wrote:
> My claim is not that conversion from time to floating point values associated with a few natural units is "properly part of the time abstraction", just that it should exist. Do you agree with that?

I refer to my reply to Jon Degenhardt which has a substantial answer to that.
November 22, 2017
Ok I understand why there is no conversion from Duration to float, but would be possible to make Duration.total not a member function? So insted of:

static mytotal(string unit)(Duration dur)
{
    return dur.total!unit;
}

alias msecs = mytotal!"msecs";

I could just add
alias msecs = total!"msecs";


On Wed, Nov 22, 2017 at 11:48 AM, Walter Bright via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On 11/22/2017 1:38 AM, Timon Gehr wrote:
>
>> My claim is not that conversion from time to floating point values associated with a few natural units is "properly part of the time abstraction", just that it should exist. Do you agree with that?
>>
>
> I refer to my reply to Jon Degenhardt which has a substantial answer to that.
>


November 22, 2017
On 11/18/17 9:03 AM, Timon Gehr wrote:

> (Jonathan M Davis from comment #4)
>  > TickDuration will soon be deprecated, and none of the other time stuff
>  > supports floating point. Anyone who wants that functionality can calculate
>  > the floating point values from the integral values that the types do
>  > provide. It's not something that most code should be doing, but the API
>  > makes it possible for those who care.
> 
> 
> "Not something that most code should be doing"? I have never used StopWatch and then /not/ wanted to do something like to!("seconds",double)!
> 
> There seems to be no good reason to break my code beyond requiring a different import here. What are the perceived shortcomings of the to!("seconds", double) interface?

My take on this, as someone who has argued extensively NOT to use double for timing calculations (I was responsible for adding the TimeSpan type in Tango (the equivalent of Duration) and marginalizing Interval (based on double) [1]):

1. All OS calls with timing requirements use non-floating point to represent how long to sleep. After all a CPU uses discrete math, and the timing implementation is no different.

2. Sometimes it is VERY important to use exact representation for everything.

  Example: I want to sleep for 1 microsecond, if you had a function that accepts a floating point value, and you pass in 0.000_001, you might actually sleep for 0 ticks, which is vastly different.

3. Sometimes it is not as important.

  Example: if you want to sleep for 1.75 seconds, having a function that converts the float representation of 1.75 into a Duration is useful, and reasonably accurate. It isn't going to ruin your application if the sleep passed to the OS is 1.749999999 seconds. Your sleep probably isn't going to be exactly accurate anyways, as most of use use non-real-time OSes.

4. There are many libraries, the most obvious one to me is Apple's core library, which use a double for representing time everywhere. So it's not without precedent.

5. Responding to Walter's one-liner resistance, if the one liner is trivial to get right, then sure, don't include it. It could even be written in-line. But if it's easy to get WRONG, or is annoying to have to write, then I think it's worth having, even if it's a one-liner.

   In my opinion, type conversion is one of those things that falls into that second category. How can I construct the most accurate Duration with a given double that is in the form of seconds? You'd have to know the representation of Duration as hectonanoseconds in order to get this right. While trivial to write once you *know* that, it's not trivial to write if you *don't*. Having a library function (even a one-liner) that says "I'll save you from the implementation details" is useful.

  related: https://github.com/dlang/druntime/pull/1927

  Bottom line: one-liner-ness shouldn't be an automatic disqualifier.

-----------

After having used Apple's SDK quite a bit, I've changed my opinion slightly since my Tango days. It is useful to have a floating point representation of time, and can be used to good effect. I would be fine with a mechanism to convert to/from double with a Duration in druntime (in fact, Tango kept this), but still want to have the representation be as accurate as possible when it comes to calling OS functions. All the public APIs of druntime/phobos should take/return only Durations, not doubles, and let the user convert if they want to use doubles elsewhere.

-Steve

[1] http://dsource.org/projects/tango/ticket/671
November 22, 2017
On Wednesday, November 22, 2017 12:45:01 Daniel Kozak via Digitalmars-d wrote:
> Ok I understand why there is no conversion from Duration to float, but would be possible to make Duration.total not a member function? So insted of:
>
> static mytotal(string unit)(Duration dur)
> {
>     return dur.total!unit;
> }
>
> alias msecs = mytotal!"msecs";
>
> I could just add
> alias msecs = total!"msecs";

How would that help anything? You can clearly create an msecs alias right now, and you could get the same effect using a free function named msecs instead of an alias. Also, core.time.msecs is already an alias for core.time.dur!"msecs", so creating your own symbol called msecs (be it an alias or a free function) could muck with your use of the one from core.time (though it's certainly possible to disambiguate between the two or to use dur!"msecs" directly).

And regardless of this specific situation, in general, turning a member function into a free function risks breaking code, since instead of it being a member function that automatically wins out with UFCS, it could suddenly be in conflict with another function of the same name, meaning that the calling code would have to then disambiguate between them whereas it didn't before. So, in general, turning member functions into free functions isn't a great idea if you're not in control over all of the code involved or otherwise aren't concerned about the potential fallout.

- Jonathan M Davis

November 22, 2017
On 11/22/2017 2:45 AM, Walter Bright wrote:
> On 11/22/2017 1:41 AM, Timon Gehr wrote:
>> Why would the conversion function be linked in if I never use it?
> 
> Good question. It depends on how the code is written, and how the compiler represents it in the object file, and how the linker deals with unreferenced parts of object files.


For another example, unreferenced virtual functions never get elided because a reference to them does exist - they're in the virtual function pointer table. And then, of course, everything that virtual function references is never elided.

Another reason to be careful of "kitchen sink" abstractions.