Modify

Ticket #6627 (closed Bugs: fixed)

Opened 2 years ago

Last modified 2 years ago

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

nonfinite_num_put_zero_formatting.patch Download (4.5 KB) - added by krwalker@… 2 years ago.
nonfinite_num_facets_formatting_2.hpp.patch Download (1.1 KB) - added by pbristow 2 years ago.
Patch to try to treat unsigned zero as normal value.

Change History

comment:1 Changed 2 years ago by johnmaddock

  • Owner changed from johnmaddock to pbristow

comment:2 Changed 2 years ago by pbristow

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

comment:3 Changed 2 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 2 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 2 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 2 years ago by krwalker@…

comment:6 Changed 2 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 2 years ago by pbristow

Patch to try to treat unsigned zero as normal value.

comment:7 follow-up: ↓ 8 Changed 2 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 2 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 2 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 2 years ago by pbristow

  • Status changed from new to closed
  • Version changed from Boost 1.48.0 to Boost 1.50.0
  • Resolution set to fixed

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.

View

Add a comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
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.