Boost logo

Boost :

Subject: Re: [boost] [contract] toward N3351 concepts
From: Andrew Sutton (asutton.list_at_[hidden])
Date: 2012-10-02 10:54:07


> That's no different from saying that a program that uses
> 2 + 3i as if it were a real number has undefined behavior.

I'm not sure that this is the same thing. What you have here is a
potential a type error. The previous question was whether NaN is a
well-formed value.

I should amend that statement: NaN is not a well-formed value when
interpreted as a real number. It is a perfectly reasonable state for a
floating point object. I had previously omitted that last part ("when
interpreted as a real number"), and I shouldn't have. It's actually
very important to understanding when the use NaN can lead to undefined
behavior and when it cannot. And why.

> If you make an assumption about the value of an object,
> then it's likely to be undefined behavior if that
> assumption doesn't hold.

That is certainly true.

> Some algorithms obviously won't work, but others like
> copy that don't depend on comparison will work fine.
> You can even use find or is_sorted if you pass a custom
> predicate that handles NaN's in a way consistent with
> what the algorithm expects. In short, the fact that
> NaN's cause undefined behavior is a property of
> the concept map of comparison for double, not
> a property of double in itself.

Algorithms where NaN is interpreted as a real number, whether through
equality, ordering, or numeric operations, result in undefined
behavior in the sense that the postconditions of the algorithm are
broken. The previous examples of find() and sort() fall into that
category.

Copy does not require NaN to be interpreted as a real number, so a
program copy()ing sequences of doubles containing NaNs will not result
in undefined behavior.

Custom predicates change the interpretation the floating point state.
The find_if() algorithm does not impose any interpretation on the
iterator's value type; the predicate does. Passing isnan() as a
predicate does not interpret the value type as a real number, just a
bit pattern. A lambda doing this: [](double n) { return n < 0.0; }
does. It may result in undefined behavior.

Similarly, a custom relation can impose alternative rules for NaN,
making it act like optional<double>. But this is not an interpretation
of NaN as a real number. It is an interpretation that extends real
numbers with an optional null, missing, or maybe state.

This has nothing to do with concept maps. That was a language feature.
This is far more fundamental.

> As far as I'm concerned, any value that can exist
> at all is a member of the set of values represented
> by the type.

The meaning of "value" is not arbitrary. The C++ community seems to
have converged on the idea the set of states we associate with a
type's "value" as those elements which can be distinguished by
equality comparison. That set of states tends to conform to some
abstraction: naturals, integers, reals, iterators (or positions),
sequences, and sets.

The notion of "value" shouldn't simply be taken as the set of valid
states encoded by a type.

> The fact that some values are forbidden
> in certain contexts is irrelevant.

I am not disagreeing that programs written using NaN are necessarily
wrong, but I do disagree that this is irrelevant. Understanding how a
type's states are interpreted, in different contexts, is important to
understanding the behavior of our programs and if (or more likely
when) we invoke undefined behavior by breaking assumptions.

Andrew


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk