Topic: precision of hexfloat output in TR1?


Author: jsalmon@thesalmons.org (John Salmon)
Date: Fri, 20 Apr 2007 07:39:54 GMT
Raw View
I am trying to understand how TR1 supports hex float (%a) output.

As far as I can tell, it does so via the following:

<snip>
8.15 Additions to header <locale> [tr.c99.locale]

In subclause 22.2.2.2.2, Table 58 Floating-point conversions, after
the line:
  floatfield == ios_base::scientific %E

add the two lines:
  floatfield == ios_base::fixed | ios_base::scientific && !uppercase %a
  floatfield == ios_base::fixed | ios_base::scientific %A 2

[Note: The additional requirements on print and scan functions, later
in this clause, ensure that the print functions generate hexadecimal
floating-point fields with a %a or %A conversion specifier, and that
the scan functions match hexadecimal floating-point fields with a %g
conversion specifier.  end note]
</snip>

Following the thread, in 22.2.2.2.2, we find:

<snip>
For conversion from a floating-point type, if (flags & fixed) != 0 or
if str.precision() > 0, then str.precision() is specified in the
conversion specification.
</snip>

This would seem to imply that when floatfield == fixed|scientific, the
precision of the conversion specifier is to be taken from
str.precision().  Is this really what's intended?  I sincerely hope
that I'm either missing something or this is an oversight.  Please
tell me that the committee did not intend to mandate that hex floats
(and doubles) should by default be printed as if by %.6a.

John Salmon

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: pjp@dinkumware.com ("P.J. Plauger")
Date: Fri, 20 Apr 2007 08:54:39 GMT
Raw View
"John Salmon" <jsalmon@thesalmons.org> wrote in message
news:m3fy6v1ztk.fsf@river.fishnet...

> I am trying to understand how TR1 supports hex float (%a) output.
>
> As far as I can tell, it does so via the following:
>
> <snip>
> 8.15 Additions to header <locale> [tr.c99.locale]
>
> In subclause 22.2.2.2.2, Table 58 Floating-point conversions, after
> the line:
>  floatfield == ios_base::scientific %E
>
> add the two lines:
>  floatfield == ios_base::fixed | ios_base::scientific && !uppercase %a
>  floatfield == ios_base::fixed | ios_base::scientific %A 2
>
> [Note: The additional requirements on print and scan functions, later
> in this clause, ensure that the print functions generate hexadecimal
> floating-point fields with a %a or %A conversion specifier, and that
> the scan functions match hexadecimal floating-point fields with a %g
> conversion specifier.  end note]
> </snip>
>
> Following the thread, in 22.2.2.2.2, we find:
>
> <snip>
> For conversion from a floating-point type, if (flags & fixed) != 0 or
> if str.precision() > 0, then str.precision() is specified in the
> conversion specification.
> </snip>
>
> This would seem to imply that when floatfield == fixed|scientific, the
> precision of the conversion specifier is to be taken from
> str.precision().  Is this really what's intended?  I sincerely hope
> that I'm either missing something or this is an oversight.  Please
> tell me that the committee did not intend to mandate that hex floats
> (and doubles) should by default be printed as if by %.6a.

Looks like a missed edit. (flags & fixed) should probably be:

((flags & (fixed | scientific)) == fixed)

Thanks for pointing out the issue.

P.J. Plauger
Dinkumware, Ltd.
http://www.dinkumware.com


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: pete@versatilecoding.com (Pete Becker)
Date: Fri, 20 Apr 2007 09:34:32 GMT
Raw View
John Salmon wrote:
>
> This would seem to imply that when floatfield == fixed|scientific, the
> precision of the conversion specifier is to be taken from
> str.precision().  Is this really what's intended?  I sincerely hope
> that I'm either missing something or this is an oversight.  Please
> tell me that the committee did not intend to mandate that hex floats
> (and doubles) should by default be printed as if by %.6a.
>

This was missed in TR1, and the problem is made worse by a change in
this area in the current working draft. I've pointed out the problem to
the appropriate people.

--

 -- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Fri, 20 Apr 2007 13:32:23 GMT
Raw View
John Salmon ha scritto:
> Following the thread, in 22.2.2.2.2, we find:
>
> <snip>
> For conversion from a floating-point type, if (flags & fixed) != 0 or
> if str.precision() > 0, then str.precision() is specified in the
> conversion specification.
> </snip>
>
> This would seem to imply that when floatfield == fixed|scientific, the
> precision of the conversion specifier is to be taken from
> str.precision().  Is this really what's intended?  I sincerely hope
> that I'm either missing something or this is an oversight.  Please
> tell me that the committee did not intend to mandate that hex floats
> (and doubles) should by default be printed as if by %.6a.

Well, in the working draft of C++0x (which includes hexfloat support)
the test on flags has been removed:

"For conversion from a floating-point type, str.precision() is specified
in the conversion specification."

Which is much worse, from my point of view, because this wording
provides no way to actually obtain %a formatting with *missing*
precision. This is really bad, because while for %g, %f and %e omitting
the precision means "precision = 6", so it makes sense to always use
str.precision() whatever its value is, for %a omitting the precision
means "the precision is sufficient for an exact representation of the
value" a result that is semantically different form %.Na whatever N is.
(Incidentally, I believe that omitting the precision is the more common
use of hexfloats).

There definitely is a need to allow the programmer to specify the
precision if he/she so wants, and using str.precision() is only
reasonable way to do it, because it's already established for %g, %f and
%e (and this won't change). Therefore I guess that having %.6a as the
default for hexfloats is the least of evils. What we are actually
missing is to allow the programmer to omit the precision entirely. One
solution I see is to amend the statement above to allow a special value
for precision, for example like this:

"For conversion from a floating-point type, if str.precision() !=
(streamsize)-1 then str.precision() is specified in the conversion
specification."

I know it's ugly to use:

  cout << hexfloat << setprecision(-1) << 3.14;

but what other solutions do we have? Well, things could be a little bit
easier if we had parametrized manipulators:

  cout << fixed(6); // equivalent to fixed << setprecision(6)
  cout << hexfloat(4); // equivalent to hexfloat << setprecision(4)
  cout << hexfloat(); // equivalent to hexfloat << setprecision(-1)

Any ideas?

Ganesh

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: John Salmon <jsalmon@thesalmons.org>
Date: Sat, 21 Apr 2007 10:18:04 CST
Raw View
AlbertoBarbati@libero.it (Alberto Ganesh Barbati) wrote:

> "For conversion from a floating-point type, if str.precision() !=
> (streamsize)-1 then str.precision() is specified in the conversion
> specification."
>
> I know it's ugly to use:
>
>   cout << hexfloat << setprecision(-1) << 3.14;
>
> but what other solutions do we have?

Since you raise the possibility of special treatment for a precision
of -1, may I suggest generalizing it slightly.  %a with no precision
has the very useful property of producing a result that can be used to
unambiguously reconstruct the original value from the hex
representation.  I.e., %a conversions permit the round-trip conversion
to be the identity.

http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1822.pdf
defines numeric_limits<T>::max_digits10 to be the value of the
precision that permits the round-trip conversion using decimal to be
the identity.

As the examples in n1822 demonstrate, max_digits10 is a very useful
value to specify for a decimal precision.  It does not guarantee the
"round trip" property, but it makes the round-trip property a QoI
issue.  I.e., if you print a value with precision equal to
max_digits10, and then scan it back, a high quality implementation can
and will give you back exactly what you started with.  The C99 standard
recommends, but does not require it to do so.  (See the appendix at
the end of this note for a precise description of what I mean here).

So with wording like this:

"For conversion from a floating-point type, if str.precision() !=
(streamsize)-1 then str.precision() is specified in the conversion
specification."

"If the precision is -1, then the precision shall be taken
from Table XXX

Table XXX:  Conversion precision when str.precision() == (streamsize)-1

   (floatfield&fixed) == 0    |    numeric_limits<T>::max_digits10
   otherwise                  |     unspecified

"

The logic of Table XXX is meant to arrange that we'd have max_digits10
precision for %g and %e conversions and unspecified precision for %a
and %f conversions.

This gives programmers a handy way to get the round-trip identity
property without having to know about or understand numeric_limits.
E.g.:

double pi_over_4;
stringstream ss;
ss << setprecision(-1) << atan2(1.,1.);
ss >> pi_over_4;
if( pi_over_4 == atan2(1.,1.) ){
   cout << "Congratulations!  Your inserter and extractor rounded pi/4 correctly\n";
}

This is arguably syntactic sugar for the programmer who knows he's
dealing with a particular type, e.g., float.  But it's essential
flavoring for the programmer who doesn't know the types his code
is going to see, e.g., library writers.

The lexical_cast function in
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1973.html
provides a good example.  The specification currently has the
following wording, which is an *attempt* to facilitate the roundtrip
property:

"If std::numeric_limits<Target>::is_specialized, the underlying stream
precision is set according to std::numeric_limits<Target>::digits10 +
1, otherwise if std::numeric_limits<Source>::is_specialized, the
underlying stream precision is set according to
std::numeric_limits<Source>::digits10 + 1."

Let's ignore for the moment the fact that this specification
incorrectly uses digits10+1 when it clearly wants max_digits10
or an equivalent value computable from std::numeric_limits members.
If we do discuss that, let's do it in a separate thread.

If precision==-1 had the meaning suggested above, this clause in n1973
could be eliminated completely, and replaced by slightly modified
wording a little higher up:

 "Effects:

  * Inserts arg into an empty std::basic_stringstream object whose
    precision has been set to -1."

In addition to simplifying the specification, this change would "fix"
the behavior of lexical_cast on composite types containing floating
types, e.g., complex<double>.

According to the original specification,

  complex<double> d;
 s = lexical_cast<string>(c);

the decimal expansions in s will have the default 6 digits of
precision - not enough to unambiguously represent the original complex
value or to permit the round-trip identity.  With the suggested
wording, the real and imaginary decimal expansions in s would have
enough digits to recover the original value exactly with a subsequent
conversion back to complex<double>, e.g, 17 in an IEEE floating point
environment.

John Salmon

Appendix:  in the hope of forestalling a long discussion of
floating conversions, here are some pointers to relevant
sections of standards, as well as a verbose description of
exactly what I mean by "permits the round-trip conversion to
be the identity".

C99 3.14 defines:
"correctly rounded result:  representation in the result format that is
nearest in value, subject to the effective rounding mode, to what the
result would be given unlimited range and precision"

C99 (6.4.4.2) requires that floating literals must be converted to
within one ulp of the nearest representable value.  It also requires
that in strtod a decimal string "... is interpreted as a floating
constant according to the rules of 6.4.4.2..."  In 7.19.6.2, the
behavior of the %e, %f %g and %a conversion specifiers in the scanf
family are defined in terms of strtod.

C99 recommends but does not require (6.4.4.2) that the translation
time conversion should match the execution time conversion performed
by library functions (strtod).  It further recommends (7.19.6.1) that
binary-internal-to-decimal-string conversion of at most DECIMAL_DIG
significant digits be "correctly rounded", and that (7.20.1.3)
decimal-string-to-binary-internal conversion of strings with at most
DECIMAL_DIG digits be correctly rounded.

If the implementation follows Recommended practice, then we can use
the analysis of n1822 and references therein to conclude that
internal-binary-to-decimal-string conversion with a precision of
numeric_limits<T>::max_digits10, followed by
decimal-string-to-internal-binary conversion with strtod, scanf("%g")
or a C++ extraction operator will return exactly the original value.
This is what I mean by "the precision that permits the round-trip
conversion to be the identity".  It's "permitted" in the sense that
there's enough information there for a high quality implementation to
actually achieve it.  Whether it truly is an identity transformation
in practice is a QoI issue.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: howard.hinnant@gmail.com (Howard Hinnant)
Date: Mon, 23 Apr 2007 20:50:15 GMT
Raw View
In article <pt-dnaESMJIjFbXbnZ2dnUVZ8q2dnZ2d@giganews.com>,
 pete@versatilecoding.com (Pete Becker) wrote:

> John Salmon wrote:
> >
> > This would seem to imply that when floatfield == fixed|scientific, the
> > precision of the conversion specifier is to be taken from
> > str.precision().  Is this really what's intended?  I sincerely hope
> > that I'm either missing something or this is an oversight.  Please
> > tell me that the committee did not intend to mandate that hex floats
> > (and doubles) should by default be printed as if by %.6a.
> >
>
> This was missed in TR1, and the problem is made worse by a change in
> this area in the current working draft. I've pointed out the problem to
> the appropriate people.

Thanks Pete.

http://home.twcny.rr.com/hinnant/cpp_extensions/issues_preview/lwg-active
.html#671

This is an unofficial preview of the issue.  When official it will
appear here:

http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html

-Howard

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Alberto Ganesh Barbati <AlbertoBarbati@libero.it>
Date: Mon, 23 Apr 2007 22:55:29 CST
Raw View
John Salmon ha scritto:
>
> "If the precision is -1, then the precision shall be taken
> from Table XXX
>
> Table XXX:  Conversion precision when str.precision() == (streamsize)-1
>
>    (floatfield&fixed) == 0    |    numeric_limits<T>::max_digits10
>    otherwise                  |     unspecified
>
> "
>

I will avoid like the plague the term "unspecified" up there, because it
might (in fact, it should) be interpreted as "the implementation can do
whatever it wants and need not even document it" (1.3.13). We definitely
want the precision to be "omitted" in the formatting specifier, as the
semantic of such "omission" is well defined in the C standard.

I like your idea, although there a minor problem: outputting either a
float or a double is eventually performed by calling the same function,
namely

   iter_type num_put::do_put(iter_type, ios_base&, char_type, double) const;

This means that floats would be output with a precision equal to
numeric_limits<double>::max_digits10 with a waste of digits. Whether
this can be considered acceptable, frankly I don't know.

Ganesh

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: jsalmon@thesalmons.org (John Salmon)
Date: Wed, 25 Apr 2007 04:07:23 GMT
Raw View
>>>>> "Ganesh" == Alberto Ganesh Barbati <AlbertoBarbati@libero.it> writes:

Ganesh> John Salmon ha scritto:
>>
>> "If the precision is -1, then the precision shall be taken
>> from Table XXX
>>
>> Table XXX:  Conversion precision when str.precision() == (streamsize)-1
>>
>> (floatfield&fixed) == 0    |    numeric_limits<T>::max_digits10
>> otherwise                  |    unspecified
>>
>> "
>>

Ganesh> I will avoid like the plague the term "unspecified" up there, because it
Ganesh> might (in fact, it should) be interpreted as "the implementation can do
Ganesh> whatever it wants and need not even document it" (1.3.13). We definitely
Ganesh> want the precision to be "omitted" in the formatting specifier, as the
Ganesh> semantic of such "omission" is well defined in the C standard.

Agreed.  "Omitted" is exactly what I meant.

Ganesh> I like your idea, although there a minor problem: outputting either a
Ganesh> float or a double is eventually performed by calling the same function,
Ganesh> namely

Ganesh>    iter_type num_put::do_put(iter_type, ios_base&, char_type, double) const;

Ganesh> This means that floats would be output with a precision equal to
Ganesh> numeric_limits<double>::max_digits10 with a waste of digits. Whether
Ganesh> this can be considered acceptable, frankly I don't know.

Easily fixed if we can add:

public:
  iter_type num_put::do_put(iter_type, ios_base&, char_type, float) const;

protected:
  virtual iter_type num_put::do_put(iter_type, ios_base&, char_type, float) const;

to [lib.facet.num.put.members] and [lib.face.num.put.virtuals].

I wonder why they weren't there to begin with?  I note that there's an
overload for operator<<(..., float) defined in
[lib.ostream.inserters.arithmetic].  It seems strange that it was
omitted from [lib.facet.num.put.members].  My guess is that the
omission of a float overload follows from reasoning along the lines: C
promotes floats to doubles in the argument list of printf, so C's
behavior can be reproduced without do_put(..., float).  But then why have
operator<<(..., float)?

What are the ramifications of adding a couple of member functions to
[lib.face.num.put]?  Is there any chance of it eventually being
considered favorably by the committee?

John Salmon

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: AlbertoBarbati@libero.it (Alberto Ganesh Barbati)
Date: Wed, 25 Apr 2007 09:37:16 GMT
Raw View
John Salmon ha scritto:
>
> Ganesh> I like your idea, although there a minor problem: outputting either a
> Ganesh> float or a double is eventually performed by calling the same function,
> Ganesh> namely
>
> Ganesh>    iter_type num_put::do_put(iter_type, ios_base&, char_type, double) const;
>
> Ganesh> This means that floats would be output with a precision equal to
> Ganesh> numeric_limits<double>::max_digits10 with a waste of digits. Whether
> Ganesh> this can be considered acceptable, frankly I don't know.
>
> Easily fixed if we can add:
>
> public:
>   iter_type num_put::do_put(iter_type, ios_base&, char_type, float) const;
>
> protected:
>   virtual iter_type num_put::do_put(iter_type, ios_base&, char_type, float) const;
>
> to [lib.facet.num.put.members] and [lib.face.num.put.virtuals].
>
> <snip>
>
> What are the ramifications of adding a couple of member functions to
> [lib.face.num.put]?  Is there any chance of it eventually being
> considered favorably by the committee?

I'm not in the committee, but I don't think that's even nearly
feasibile, because it might change the behaviour of exisisting valid
code. Facets are designed so that they can be re-implemented in the
application. Suppose I write my custom facet derived from num_put that
does some fancy formatting of doubles and long doubles. Now it works for
floats too, but after your proposed change that would no longer be true:
floats would be hijacked by the new virtual function.

Just my opinion,

Ganesh

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]