[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
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
asked
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.
I've tried to read your entire reply carefully, but I still can't find
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
- [lmi] Is bourn_cast demonstrably correct?, Greg Chicares, 2017/03/19
- Re: [lmi] Is bourn_cast demonstrably correct? (+PATCH), Vadim Zeitlin, 2017/03/20
- [lmi] Two kinds of precision loss [Was: Is bourn_cast demonstrably correct?], Greg Chicares, 2017/03/21
- Re: [lmi] Two kinds of precision loss, Vadim Zeitlin, 2017/03/22
- Re: [lmi] Two kinds of precision loss, Greg Chicares, 2017/03/22
- Re: [lmi] Two kinds of precision loss, Vadim Zeitlin, 2017/03/22
- Re: [lmi] Two kinds of precision loss, Greg Chicares, 2017/03/24
- [lmi] Is DBL_MAX "adjacent" to infinity? [Was: Two kinds of precision loss], Greg Chicares, 2017/03/24
- Re: [lmi] Is DBL_MAX "adjacent" to infinity?, Vadim Zeitlin, 2017/03/24
- Re: [lmi] Is DBL_MAX "adjacent" to infinity?, Greg Chicares, 2017/03/24