C++ Standard Library
  1. C++ Standard Library
  2. STDCXX-51

infinity, NaN formatted differently on different platforms

    Details

    • Type: Improvement Improvement
    • Status: Reopened
    • Priority: Minor Minor
    • Resolution: Unresolved
    • Affects Version/s: 4.1.2, 4.1.3, 4.1.4, 4.2.0, 4.2.1
    • Fix Version/s: 4.2.2
    • Component/s: 22. Localization
    • Labels:
      None
    • Environment:

      all

    • Patch Info:
      Patch Available
    • Severity:
      Incorrect Behavior

      Description

      The output of the program below is different depending on the operating system it runs on. It should be the same (preferably like that on AIX).

      $ cat u.cpp && uname -sr && make u -r && ./u
      #include <iostream>
      #include <limits>
      
      int main ()
      {
          std::cout << std::numeric_limits<double>::infinity () << '\n'
                    << std::numeric_limits<double>::quiet_NaN () << '\n'
                    << std::numeric_limits<double>::signaling_NaN () << '\n'; 
      }
      SunOS 5.9
      gcc -c -I/build/sebor/dev/stdlib/include/ansi -D_RWSTDDEBUG   -pthreads -D_RWSTD_USE_CONFIG -I/build/sebor/gcc-3.4.3-15s/include -I/build/sebor/dev/stdlib/include -I/build/sebor/dev/stdlib/examples/include  -pedantic -nostdinc++ -g  -Wall -W -Wcast-qual -Winline -Wshadow -Wwrite-strings -Wno-long-long  u.cpp
      gcc u.o -o u -pthreads -L/build/sebor/gcc-3.4.3-15s/lib -lstd15s  -lsupc++ -lm
      rm u.o
      inf
      nan
      nan
      
      $ uname -vs && gmake u -r && ./u
      AIX 5
      xlCcore_r -c -I/build/sebor/dev/stdlib/include/ansi -D_RWSTDDEBUG    -D_RWSTD_USE_CONFIG -I/build/sebor/vacpp-7.0.0.3-15D/include -I/build/sebor/dev/stdlib/include -I/build/sebor/dev/stdlib/examples/include  -g  -q64  -qtemplateregistry=u.ti   u.cpp
      xlCcore_r u.o -o u -I/build/sebor/dev/stdlib/include/ansi -D_RWSTDDEBUG    -D_RWSTD_USE_CONFIG -liconv -brtl   -q64 -I/build/sebor/dev/stdlib/include/ansi -D_RWSTDDEBUG    -D_RWSTD_USE_CONFIG -I/build/sebor/vacpp-7.0.0.3-15D/include -I/build/sebor/dev/stdlib/include -I/build/sebor/dev/stdlib/examples/include  -qtemplateregistry=u.ti -L/build/sebor/vacpp-7.0.0.3-15D/lib -lstd15D   -lm
      rm u.o
      inf
      nanq
      nans
      
      1. num_put.diff
        10 kB
        Farid Zaripov

        Issue Links

          Activity

          Hide
          Martin Sebor added a comment -

          See also the linked issue. It may be difficult to fix completely due to platform bugs – see STDCXX-463.

          Show
          Martin Sebor added a comment - See also the linked issue. It may be difficult to fix completely due to platform bugs – see STDCXX-463 .
          Hide
          Farid Zaripov added a comment -

          I think we should use the native platform specific functions (i.e. _finite(), _isnan(), _fpclass() for MSVC and so on) to detect the infinite and NaN values instead of fixing the string result, produced by snprintf().

          Show
          Farid Zaripov added a comment - I think we should use the native platform specific functions (i.e. _finite(), _isnan(), _fpclass() for MSVC and so on) to detect the infinite and NaN values instead of fixing the string result, produced by snprintf().
          Hide
          Martin Sebor added a comment -

          You mean do our own formatting outside of sprintf() for Infinity and NaN? That's certainly a possible approach. It might even make sense to avoid using sprintf() altogether.

          Show
          Martin Sebor added a comment - You mean do our own formatting outside of sprintf() for Infinity and NaN? That's certainly a possible approach. It might even make sense to avoid using sprintf() altogether.
          Hide
          Martin Sebor added a comment -

          Which, incidentally, would be another way to dispose of STDCXX-2.

          Show
          Martin Sebor added a comment - Which, incidentally, would be another way to dispose of STDCXX-2 .
          Hide
          Farid Zaripov added a comment -

          The proposed patch attached.

          ChangeLog:

          • src/num_put.cpp (__rw_isinfnan): New function to detect inf and nan values.
            (__rw_isinf): New function to detect inf values.
            (__rw_isneginf): New function to detect negative inf values.
            (__rw_isnan): New function to detect nan values.
            (__rw_isnegnan): New function to detect negative nan values.
            (__rw_isqnan): New function to detect quiet nan values.
            (__rw_issnan): New function to detect signaling nan values.
            (__rw_fmat_infnan): New function to format inf and nan values.
            (__rw_put_num): Use __rw_isinfnan() and __rw_fmat_infnan() to format inf and nan values.
          Show
          Farid Zaripov added a comment - The proposed patch attached. ChangeLog: src/num_put.cpp (__rw_isinfnan): New function to detect inf and nan values. (__rw_isinf): New function to detect inf values. (__rw_isneginf): New function to detect negative inf values. (__rw_isnan): New function to detect nan values. (__rw_isnegnan): New function to detect negative nan values. (__rw_isqnan): New function to detect quiet nan values. (__rw_issnan): New function to detect signaling nan values. (__rw_fmat_infnan): New function to format inf and nan values. (__rw_put_num): Use __rw_isinfnan() and __rw_fmat_infnan() to format inf and nan values.
          Hide
          Farid Zaripov added a comment -

          Some platforms doesn't distinct the quiet NaN and signaling NaN, some other doesn't detect sign in NaN (negative NaN == positive NaN).
          So for the consistency of the formatting on the different platforms, we need quiet and signaling NaN as just "nan", and treat all NaN's as positive NaN's.

          Show
          Farid Zaripov added a comment - Some platforms doesn't distinct the quiet NaN and signaling NaN, some other doesn't detect sign in NaN (negative NaN == positive NaN). So for the consistency of the formatting on the different platforms, we need quiet and signaling NaN as just "nan", and treat all NaN's as positive NaN's.
          Hide
          Martin Sebor added a comment -

          I think this is the right approach. At some point we will probably want to move these functions into their own platform-specific header (analogous to the _config-*.h headers and to what you're doing with _atomic.h).

          A few suggestions regarding names:

          1. I think we might want to change __rw_isinfnan to __rw_isfinite for consistency with C99.
          2. Similarly, I suggest to replace the __rw_isneginf, __rw_isposinf, __rw_isnegnan and __rw_isposnan functions with __rw_signbit and their uses with, for example __rw_isinf(x) && 0 > __rw_signbit(x). Besides the names being consistent with C99 breaking them up like this should also make __rw_fmat_infnan slightly more efficient in that the function won't need to be testing for the special values repeatedly.
          3. Lastly, in the same vein as (1), I would suggest to rename __rw_fmat_infnan to __rw_fmat_infinite since infinite (i.e., !isfinite(x)) implies that x is either an Infinity or NaN.
          Show
          Martin Sebor added a comment - I think this is the right approach. At some point we will probably want to move these functions into their own platform-specific header (analogous to the _config-*.h headers and to what you're doing with _atomic.h ). A few suggestions regarding names: I think we might want to change __rw_isinfnan to __rw_isfinite for consistency with C99. Similarly, I suggest to replace the __rw_isneginf , __rw_isposinf , __rw_isnegnan and __rw_isposnan functions with __rw_signbit and their uses with, for example __rw_isinf(x) && 0 > __rw_signbit(x) . Besides the names being consistent with C99 breaking them up like this should also make __rw_fmat_infnan slightly more efficient in that the function won't need to be testing for the special values repeatedly. Lastly, in the same vein as (1), I would suggest to rename __rw_fmat_infnan to __rw_fmat_infinite since infinite (i.e., !isfinite(x) ) implies that x is either an Infinity or NaN.
          Hide
          Farid Zaripov added a comment -

          Resolved (possibly not for all platforms) thus: http://svn.apache.org/viewvc?rev=646396&view=rev

          Show
          Farid Zaripov added a comment - Resolved (possibly not for all platforms) thus: http://svn.apache.org/viewvc?rev=646396&view=rev
          Hide
          Farid Zaripov added a comment -
          Show
          Farid Zaripov added a comment - Merged in 4.2.x branch thus: http://svn.apache.org/viewvc?view=rev&revision=648752
          Hide
          Martin Sebor added a comment -

          As the regression test 27.ostream.inserters.stdcxx-51.cpp shows, this is still inconsistent.

          I believe the output we want is this (including sensitivity to ios::uppercase):

          Number Positive Negative
          Infinity inf / INF -inf / -INF
          Quiet NaN qnan / QNAN -nan / -QNAN
          Signaling NaN snan / SNAN -snan / -SNAN

          That is, we want to include the sign in all cases (HP-UX on PA is known to lose the sign in computations, but that's not a problem for formatting).

          Show
          Martin Sebor added a comment - As the regression test 27.ostream.inserters.stdcxx-51.cpp shows, this is still inconsistent. I believe the output we want is this (including sensitivity to ios::uppercase ): Number Positive Negative Infinity inf / INF -inf / -INF Quiet NaN qnan / QNAN -nan / -QNAN Signaling NaN snan / SNAN -snan / -SNAN That is, we want to include the sign in all cases (HP-UX on PA is known to lose the sign in computations, but that's not a problem for formatting).
          Hide
          Martin Sebor added a comment -

          The test output on Solaris/x86 is as follows:

          Expected (float):
               0.0:  0
            +/-1.0:  1 -1
            +/-INF:  inf -inf
            +/-QNAN: qnan -qnan
            +/-SNAN: snan -snan
          
          Got:
               0.0:  0
            +/-1.0:  1 -1
            +/-INF:  inf -inf
            +/-QNAN: nan nan
            +/-SNAN: nan nan
          
          Expected (float):
               0.0:  0
            +/-1.0:  1 -1
            +/-INF:  INF -INF
            +/-QNAN: QNAN -QNAN
            +/-SNAN: SNAN -SNAN
          
          Got:
               0.0:  0
            +/-1.0:  1 -1
            +/-INF:  INF -INF
            +/-QNAN: NAN NAN
            +/-SNAN: NAN NAN
          
          Expected (double):
               0.0:  0
            +/-1.0:  1 -1
            +/-INF:  inf -inf
            +/-QNAN: qnan -qnan
            +/-SNAN: snan -snan
          
          Got:
               0.0:  0
            +/-1.0:  1 -1
            +/-INF:  inf -inf
            +/-QNAN: nan nan
            +/-SNAN: nan nan
          
          Expected (double):
               0.0:  0
            +/-1.0:  1 -1
            +/-INF:  INF -INF
            +/-QNAN: QNAN -QNAN
            +/-SNAN: SNAN -SNAN
          
          Got:
               0.0:  0
            +/-1.0:  1 -1
            +/-INF:  INF -INF
            +/-QNAN: NAN NAN
            +/-SNAN: NAN NAN
          
          Expected (long double):
               0.0:  0
            +/-1.0:  1 -1
            +/-INF:  inf -inf
            +/-QNAN: qnan -qnan
            +/-SNAN: snan -snan
          
          Got:
               0.0:  0
            +/-1.0:  1 -1
            +/-INF:  inf -inf
            +/-QNAN: -nan nan
            +/-SNAN: nan -nan
          
          Expected (long double):
               0.0:  0
            +/-1.0:  1 -1
            +/-INF:  INF -INF
            +/-QNAN: QNAN -QNAN
            +/-SNAN: SNAN -SNAN
          
          Got:
               0.0:  0
            +/-1.0:  1 -1
            +/-INF:  INF -INF
            +/-QNAN: -NAN NAN
            +/-SNAN: NAN -NAN
          
          Assertion failed: pass, file /amd/devco/sebor/stdcxx-4.2.x/tests/regress/27.ostr
          eam.inserters.stdcxx-51.cpp, line 123
          Abort (core dumped)
          
          Show
          Martin Sebor added a comment - The test output on Solaris/x86 is as follows: Expected (float): 0.0: 0 +/-1.0: 1 -1 +/-INF: inf -inf +/-QNAN: qnan -qnan +/-SNAN: snan -snan Got: 0.0: 0 +/-1.0: 1 -1 +/-INF: inf -inf +/-QNAN: nan nan +/-SNAN: nan nan Expected (float): 0.0: 0 +/-1.0: 1 -1 +/-INF: INF -INF +/-QNAN: QNAN -QNAN +/-SNAN: SNAN -SNAN Got: 0.0: 0 +/-1.0: 1 -1 +/-INF: INF -INF +/-QNAN: NAN NAN +/-SNAN: NAN NAN Expected (double): 0.0: 0 +/-1.0: 1 -1 +/-INF: inf -inf +/-QNAN: qnan -qnan +/-SNAN: snan -snan Got: 0.0: 0 +/-1.0: 1 -1 +/-INF: inf -inf +/-QNAN: nan nan +/-SNAN: nan nan Expected (double): 0.0: 0 +/-1.0: 1 -1 +/-INF: INF -INF +/-QNAN: QNAN -QNAN +/-SNAN: SNAN -SNAN Got: 0.0: 0 +/-1.0: 1 -1 +/-INF: INF -INF +/-QNAN: NAN NAN +/-SNAN: NAN NAN Expected (long double): 0.0: 0 +/-1.0: 1 -1 +/-INF: inf -inf +/-QNAN: qnan -qnan +/-SNAN: snan -snan Got: 0.0: 0 +/-1.0: 1 -1 +/-INF: inf -inf +/-QNAN: -nan nan +/-SNAN: nan -nan Expected (long double): 0.0: 0 +/-1.0: 1 -1 +/-INF: INF -INF +/-QNAN: QNAN -QNAN +/-SNAN: SNAN -SNAN Got: 0.0: 0 +/-1.0: 1 -1 +/-INF: INF -INF +/-QNAN: -NAN NAN +/-SNAN: NAN -NAN Assertion failed: pass, file /amd/devco/sebor/stdcxx-4.2.x/tests/regress/27.ostr eam.inserters.stdcxx-51.cpp, line 123 Abort (core dumped)
          Hide
          Martin Sebor added a comment -

          Scheduled full fix for 4.2.2.

          Show
          Martin Sebor added a comment - Scheduled full fix for 4.2.2.
          Hide
          Martin Sebor added a comment -

          It will take some time to implement across all platforms.

          Show
          Martin Sebor added a comment - It will take some time to implement across all platforms.
          Hide
          Farid Zaripov added a comment -

          I've enabled processing the sign of the NaN's here: http://svn.apache.org/viewvc?rev=660492&view=rev

          Also the INFINITY.cpp configuration test calculated the negative qnan value. Fixed thus: http://svn.apache.org/viewvc?rev=660486&view=rev

          Show
          Farid Zaripov added a comment - I've enabled processing the sign of the NaN's here: http://svn.apache.org/viewvc?rev=660492&view=rev Also the INFINITY.cpp configuration test calculated the negative qnan value. Fixed thus: http://svn.apache.org/viewvc?rev=660486&view=rev
          Hide
          Farid Zaripov added a comment -

          On x86 platform copying of the floating-point value using fld + fstp assembly commands converting the snan value to qnan.

          So the __rw_fmat_infinite() on compilers, that uses fld+fstp (i.e. MSVC), will always print qnan when snan value is passed, because of the parameter val passed by value.

          Show
          Farid Zaripov added a comment - On x86 platform copying of the floating-point value using fld + fstp assembly commands converting the snan value to qnan. So the __rw_fmat_infinite() on compilers, that uses fld+fstp (i.e. MSVC), will always print qnan when snan value is passed, because of the parameter val passed by value.
          Hide
          Martin Sebor added a comment -

          Interesting. Unless there's a compiler switch or pragma to preserve the sign iostreams will never be able to print signaling NaN on Win32 because the num_put facet takes the numeric arguments by value.

          FYI: I'm putting together a review of the bit patterns used to represent Infinity and NaN on major platforms along with some other useful bits of info, including the libc constants for each number and the format of each produced by printf().

          Show
          Martin Sebor added a comment - Interesting. Unless there's a compiler switch or pragma to preserve the sign iostreams will never be able to print signaling NaN on Win32 because the num_put facet takes the numeric arguments by value. FYI: I'm putting together a review of the bit patterns used to represent Infinity and NaN on major platforms along with some other useful bits of info, including the libc constants for each number and the format of each produced by printf() .
          Hide
          Farid Zaripov added a comment -

          From: http://developer.intel.com/design/pentium/manuals/24319001.pdf

          As a general rule, when a QNaN is used in one or more arithmetic floating-point instructions, it
          is allowed to propagate through a computation. An SNaN on the other hand causes a floatingpoint
          invalid-operation exception to be signaled. SNaNs are typically used to trap or invoke an
          exception handler. They must be inserted by software; that is, the FPU never generates an SNaN
          as a result.

          The floating-point invalid-operation exception has a flag and a mask bit associated with it in the
          FPU status and control registers, respectively (see Section 7.7., "Floating-Point Exception
          Handling"). The mask bit determines how the FPU handles an SNaN value. If the floating-point
          invalid-operation mask bit is set, the SNaN is converted to a QNaN by setting the most-significant
          fraction bit of the value to 1. The result is then stored in the destination operand and the
          floating-point invalid-operation flag is set. If the invalid-operation mask is clear, a floating-point
          invalid-operation fault is signaled and no result is stored in the destination operand.

          Show
          Farid Zaripov added a comment - From: http://developer.intel.com/design/pentium/manuals/24319001.pdf As a general rule, when a QNaN is used in one or more arithmetic floating-point instructions, it is allowed to propagate through a computation. An SNaN on the other hand causes a floatingpoint invalid-operation exception to be signaled. SNaNs are typically used to trap or invoke an exception handler. They must be inserted by software; that is, the FPU never generates an SNaN as a result . The floating-point invalid-operation exception has a flag and a mask bit associated with it in the FPU status and control registers, respectively (see Section 7.7., "Floating-Point Exception Handling"). The mask bit determines how the FPU handles an SNaN value. If the floating-point invalid-operation mask bit is set, the SNaN is converted to a QNaN by setting the most-significant fraction bit of the value to 1. The result is then stored in the destination operand and the floating-point invalid-operation flag is set. If the invalid-operation mask is clear, a floating-point invalid-operation fault is signaled and no result is stored in the destination operand.

            People

            • Assignee:
              Martin Sebor
              Reporter:
              Martin Sebor
            • Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:

                Time Tracking

                Estimated:
                Original Estimate - 13h Original Estimate - 13h
                13h
                Remaining:
                Time Spent - 13h Remaining Estimate - 8h
                8h
                Logged:
                Time Spent - 13h Remaining Estimate - 8h
                13h

                  Development