Opened 11 years ago

Closed 11 years ago

#6627 closed Bugs (fixed)

nonfinite_num_put formatting of 0.0 is incorrect

Reported by: krwalker@… Owned by: Paul A. Bristow
Milestone: To Be Determined Component: math
Version: Boost 1.50.0 Severity: Problem
Keywords: Cc:

Description

We noticed time strings changed from displaying 00:00.00 to 00:00000 after installing the nonfinite_num facets globally. It appears that formatting for 0.0 isn't behaving as it should when precision and fixed are involved (and potentially others).

#include <boost/math/special_functions/nonfinite_num_facets.hpp>
#include <iomanip>
#include <iostream>
#include <sstream>

void writeZero( std::ostream& stream ) {
  stream << std::fixed << std::setw( 5 ) << std::setfill( '0' ) << std::setprecision( 2 ) << 0.0;
}

int main() {
  std::stringstream standardStream;
  writeZero( standardStream );
  std::cout << standardStream.str() << std::endl;

  std::stringstream nonfiniteStream;
  std::locale nonfiniteNumLocale( std::locale(), new boost::math::nonfinite_num_put< char >() );
  nonfiniteStream.imbue( nonfiniteNumLocale );

  writeZero( nonfiniteStream );
  std::cout << nonfiniteStream.str() << std::endl;

  return 0;
}

// OUTPUT:
//
// 00.00
// 00000

Attachments (2)

nonfinite_num_put_zero_formatting.patch (4.5 KB ) - added by krwalker@… 11 years ago.
nonfinite_num_facets_formatting_2.hpp.patch (1.1 KB ) - added by Paul A. Bristow 11 years ago.
Patch to try to treat unsigned zero as normal value.

Download all attachments as: .zip

Change History (12)

comment:1 by John Maddock, 11 years ago

Owner: changed from John Maddock to Paul A. Bristow

comment:2 by Paul A. Bristow, 11 years ago

OK, looks to be a bug. Will look at this in a day or few.

comment:3 by Paul A. Bristow, 11 years ago

Well this looks a bit more complicated than I feared. There seems to be a difference between sending to cout (00000) and a stringstream (00.00).

 cout << std::fixed << std::setw( 5 ) << std::setfill( '0' ) << std::setprecision( 2 ) << 0.0 << endl; // 00000

  std::stringstream astream;
  astream << std::fixed << std::setw( 5 ) << std::setfill( '0' ) << std::setprecision( 2 ) << 0.0; // 00.00

which leaves me more than a bit wary of making any precipitate changes to Johan Rade's nonfinite_num_put code :-( (Before fools step in where angels fear to tread? - again!)

I'd like you to consider if it might be safer to circumnavigate this 'feature'?

Paul

comment:4 by krwalker@…, 11 years ago

Do you have those reversed?

int main() {
  std::locale::global( std::locale( std::locale(),
    new boost::math::nonfinite_num_put< char >() ) );

  writeZero( std::cout );
  std::cout << std::endl; // 00.00

  std::stringstream astream;
  writeZero( astream );
  std::cout << astream.str() << std::endl; // 00000

  return 0;
}

"Changing the global locale does not change the locales of pre-existing streams. If you want to imbue the new global locale on cout, you should call std::cout.imbue(locale()) after calling std::locale::global()." -- http://stdcxx.apache.org/doc/stdlibug/24-3.html

comment:5 by krwalker@…, 11 years ago

I may have a patch that makes the FP_ZERO case only output the '-' if ( flags_ & signed_zero ). It then reduces iosb.width() by 1 and delegates to std::num_put.

by krwalker@…, 11 years ago

comment:6 by Paul A. Bristow, 11 years ago

OK - if you are keen to fix this properly, I'll try to get to look at this more closely, including writing all the several test cases to check I haven't introduced another bug in fixing this one. You could also email me privately.

by Paul A. Bristow, 11 years ago

Patch to try to treat unsigned zero as normal value.

comment:7 by Paul A. Bristow, 11 years ago

I have yet to fully understand how this code is intended to work.

You patch seems rather complicated?

Does my 2nd patch (that simply aims to treat an unsigned zero as a normal value) work for you ?

(While I write some tests).

in reply to:  7 comment:8 by Paul A. Bristow, 11 years ago

Replying to pbristow:

I have now written a much larger collection of tests (though there are an almost infinite number of possible combinations of ostream options like precision, width, letf right, internal, showpos showpoint :-(

And committed a revised version (using code from KR Walker) that passes on MSVC 10 these tests with the signed_zero flag set (and also handles inf and nan as before).

I am not entirely convinced that there is really a need for a signed_zero flag at all.

Are there really any platforms/libraries still out there that do not output negative zero correctly as -0?

Feedback welcome.

comment:9 by Paul A. Bristow, 11 years ago

Tests reveal that, for some combinations of options, there is a difference in output between Dinkumware STL and other STL implementations, for example:

#if defined(_CPPLIB_VER) && (_CPPLIB_VER >= 306) Dinkumware outputs "0.00"

CHECKOUT(std::showpoint << 0., "0.000000"); std::setprecision(6) CHECKOUT(std::setprecision(2) << std::showpoint << 0., "0.00");

#else others output "0.0"

CHECKOUT(std::showpoint << 0., "0.00000"); std::setprecision(6) CHECKOUT(std::setprecision(2) << std::showpoint << 0., "0.0");

#endif

I am unclear if either of these is 'wrong' according the C/C++ Standards, but this is outside control of Boost.Math code, so I propose to declare this fixed in trunk.

(There are also some STL libraries that use two exponent digits rather than three - this is standards conformant).

comment:10 by Paul A. Bristow, 11 years ago

Resolution: fixed
Status: newclosed
Version: Boost 1.48.0Boost 1.50.0

Since the C++ IO Library Standard is somewhat permissive in output, and the support for signed zero is platform dependent, it is not believed possible to provide a solution which is exactly portable (producing idential output) over all platforms. It is probably wise to avoid using the signed_zero flag.

Note: See TracTickets for help on using tickets.