Modify

Opened 5 years ago

Closed 5 years ago

#6627 closed Bugs (fixed)

nonfinite_num_put formatting of 0.0 is incorrect

Reported by: krwalker@… Owned by: pbristow
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@… 5 years ago.
nonfinite_num_facets_formatting_2.hpp.patch (1.1 KB) - added by pbristow 5 years ago.
Patch to try to treat unsigned zero as normal value.

Download all attachments as: .zip

Change History (12)

comment:1 Changed 5 years ago by johnmaddock

  • Owner changed from johnmaddock to pbristow

comment:2 Changed 5 years ago by pbristow

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

comment:3 Changed 5 years ago by pbristow

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 Changed 5 years ago by krwalker@…

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 Changed 5 years ago by krwalker@…

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.

Changed 5 years ago by krwalker@…

comment:6 Changed 5 years ago by pbristow

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.

Changed 5 years ago by pbristow

Patch to try to treat unsigned zero as normal value.

comment:7 follow-up: Changed 5 years ago by pbristow

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).

comment:8 in reply to: ↑ 7 Changed 5 years ago by pbristow

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 Changed 5 years ago by pbristow

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 Changed 5 years ago by pbristow

  • Resolution set to fixed
  • Status changed from new to closed
  • Version changed from Boost 1.48.0 to Boost 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.

Add Comment

Modify Ticket

Change Properties
Set your email in Preferences
Action
as closed The owner will remain pbristow.
The resolution will be deleted. Next status will be 'reopened'.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.