lmi
[Top][All Lists]

## Re: [lmi] Is bourn_cast demonstrably correct?

 From: Vadim Zeitlin Subject: Re: [lmi] Is bourn_cast demonstrably correct? Date: Tue, 21 Mar 2017 14:07:43 +0100

```On Mon, 20 Mar 2017 23:56:18 +0000 Greg Chicares <address@hidden> wrote:

GC> On 2017-03-20 20:46, Vadim Zeitlin wrote:
GC> > On Mon, 20 Mar 2017 20:04:03 +0000 Greg Chicares <address@hidden> wrote:
GC> [...]
GC> > GC> BTW, let me share my short-term plans for this facility. First, I
GC> > GC> myself whether it should just forbid floating point; the answer is no,
GC> > GC> because we really do have code that converts double to integral types
GC> > GC> using value_cast<>(), and the whole point of bourn_cast<>() is to
GC> > GC> replace boost::numeric_cast<>() in value_cast<>().
GC> >
GC> >  Is the code doing this really supposed to lose precision? I.e. should it
GC> > convert M_PI to 3? Or is it only meant to convert 3.0 to 3 and throw for
GC> > the numbers with non zero fractional part?
GC>
GC> A long time ago, I wrote this:
GC>
GC> /// Function template numeric_value_cast() wraps boost::numeric_cast
GC> /// to make it DWISOTT according to the boost-1.31.0 documentation:
GC> ///   "An exception is thrown when a runtime value-preservation
GC> ///   check fails."
GC> /// The problem is that
GC> ///   boost::numeric_cast<int>(2.71828);
GC> /// returns the integer 2 without throwing, but 2.71828 and 2 are
GC> /// different values. It seems unreasonable to call truncation a
GC> /// value-preserving relation.

FWIW I totally agree with the above.

GC> Now I believe the flaw was in the documentation, not the code.

what exactly motivated your change of mind.

GC> I can't be certain what the documentation should have said, but,
GC> as Cacciola's paper:
GC>   http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1879.htm
GC> explains, there are two problems in mapping between floating and
GC> integral types:
GC>
GC>   (1) loss of range: e.g., DBL_MAX --> int, where static_cast produces
GC> undefined behavior [4.9/1] and definitely gives a wrong answer, because
GC> there really is no right answer;
GC>
GC>   (2) loss of precision: e.g., M_PI --> int, or ULLONG_MAX --> float,
GC> where static_cast gives a well-defined result [4.9/2] that is as good
GC> an approximation as it can be, plus or minus one ulp;
GC>
GC> and Henney's real intention was probably to throw on (1) and pass (2)
GC> through without complaint: IOW, to write a static_cast wrapper that
GC> simply blocks obvious pitfalls like casting -1U into FFFFFFFF.

I can't judge what Henney's intention was, but I don't think it's really
important, why should we do exactly the same thing as he wanted to, anyhow?
My position is that bourn_cast<> looks like a safe cast and I wouldn't
expect it to silently truncate values, especially as there doesn't seem to
be any situation in which this would be useful -- but it's easy to imagine
a situation in which it could happen unexpectedly/accidentally. And there
is also another facility in lmi which can, and should, be used when we
really need to round a floating point number to an integer, so why should
this cast do it too?

GC> IOW, we've used numeric_value_cast() broadly, but we have never seen
GC> it throw an "inexact" exception, so, arguably, the runtime value-
GC> preservation check provides no actual benefit, and might be eliminated;
GC> but OTOH it's cheap insurance, so why throw it away?

For me this can be interpreted in exactly the opposite way: if the
"inexact" exception has never been seen, it means that we only ever use
numeric_value_cast<int>(double) with values which are integers and so it
makes a lot of sense to continue to check that this is the case.

GC> I think the best answer is to keep bourn_cast as a "guarded" wrapper
GC> for static_cast, which throws only when the behavior of static_cast
GC> is surprising (-1U --> FFFFFFFF) or undefined (DBL_MAX --> int); and
GC> to provide a wrapper-wrapper like numeric_value_cast() above if value
GC> preservation is important.

I'd prefer to turn it around and use bourn_cast<> for integers only and
have some round_cast<> for the floating point numbers if necessary (right
now it doesn't seem to be needed at all, however, unless I'm missing
something).

GC> Hmmm...in that wrapper-wrapper:
GC>     To result = boost::numeric_cast<To>(from);
GC>     if(result == from)
GC> is "result == from" the best we can do

I think so, at least I don't see any situation in which it wouldn't do the
right thing (assuming "from" is a floating point value and "result" an
integer one).

Regards,
VZ

```