
David Abrahams wrote:
"Robert Ramey" <ramey@rrsd.com> writes:
David Abrahams wrote:
"Robert Ramey" <ramey@rrsd.com> writes:
I think we should go in the opposite direction. A float is a a legitmate floating point value. A union of float and some other special non-floating point values is something else.
Notwithstanding the fact that NaN is "not a number," it is a legitimate floating-point value, i.e. a legitimate value for the type float.
I guess it depends what one means by "legitimate". It is is certainly "legal" in C++ - no question about that fact. Its certainly not a number - no question about that either. The problem is that C++ makes float a union of two different things
Not any more than a pointer is a "union of two different things." There are quite a few operations on pointers that suddenly become verboten when the pointer is null, and even more such operations when the pointer is dangling.
pointers make no pretense to implement the operations of another system - in this case arithmetic. The concept of a pointer being the "union of two different things" is intuitively acceptable while its not for something meant to model a real number.
and that is what creates all the problems we have with it. C++ should correct this by making the result of arithmetic operations which result in Nan's either undefined or throw exceptions. Oh I know that this would break a lot of existing code.
Not only break, but break unfixably. There are important numerical applications where large collections of individual floating values (e.g. vectors and matrices) have to be computed in parallel, and one or more NaNs in the result do not render the whole calculation useless.
I suppose that's possible. I'm not familiar with such applications. It sounds to me that they would be implemented in special hardware and sort of out of the C++ mainstream.
Besides all that, eliminating NaNs from the serialization problem space is really impossible. Even if we struck them from the C++ language, someone would effectively recreate them using optional<float> or the equivalent.
Actually that would be an improvement in that the "union" would be explicit, visible and modifiable. In fact I could see the utility right now of a "safe_float" which would look something like #ifndef NDEBUG BOOST_STRONG_TYPE(float, safe_float) safe_float operator/(safe_float x, safe_float y){ if(y < epsilon) // machine dependent epsilon throw overflow_exception return x / y; } ... #else #define safe_float float #endif
I would argue that such code is already broken anyway - it just looks like its working.
Really, you still think so?
My view on this is in another post.
If so, I'd like to know how you measure "brokenness."
A program which produces an undefined result is "broke" BTW - as far as the serialization system is concerned I never had a problem with the idea of recovering the exact kind of "undefined" data. Its just that there's no way to do it with archives which might be ported from one machine to another as there is no guarentee that the reading machine has the same set of of undefined results as the source machine. Its even worse, there is way to write portable code which will generate any specific one of the "undefined" types. One might hack something together that would recover some undefined type but it wouldn't be guarenteed to the the same original type of undefined type. So the whole effort would be for naught. There was a discussion of this on the list a while ago and this was the conclusion. It was in the course of this discussion that I reached the conclusions I've stated here. Robert Ramey