]> git.saurik.com Git - wxWidgets.git/commitdiff
Add IEEE 754 single/double precision support to wxDataStream classes.
authorVadim Zeitlin <vadim@wxwidgets.org>
Mon, 6 May 2013 00:31:03 +0000 (00:31 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Mon, 6 May 2013 00:31:03 +0000 (00:31 +0000)
Allow to optionally raed/write float/double values in IEEE 754 single/double
precision formats, respectively, instead of always using the extended
precision format for both of them.

This makes the code more flexible, allowing for better interoperability with
the other programs, and also allows to implement floating point functions in
these classes even when wxUSE_APPLE_IEEE is turned off (as can be the case
because of the licencing concerns for the code in extended.c).

Closes #10625.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@73938 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/changes.txt
docs/doxygen/mainpages/const_wxusedef.h
include/wx/datstrm.h
interface/wx/datstrm.h
src/common/datstrm.cpp
tests/streams/datastreamtest.cpp

index 2dec0578205159ccf4a00ca2bb0a7faa422f41eb..c8f9466d1143e5f52fd564ff4de3544c9b8af569 100644 (file)
@@ -589,6 +589,7 @@ All:
 - Add wxVector::assign() (Jonas Rydberg).
 - Add wx[F]File{Input,Output}Stream::GetFile() (troelsk).
 - Add wxSocketBase::GetSocket() (Laurent Poujoulat).
+- Add IEEE 754 single/double precision support to wxDataStream classes (net147).
 - Add Nepali translation (Him Prasad Gautam).
 
 All (GUI):
index 4127c8c7147ac068fdb3263d0f6e48c23389252d..8feb863c1ef1240ba95e7335ce5ae820ff871a63 100644 (file)
@@ -59,7 +59,7 @@ library:
 @itemdef{wxUSE_ARTPROVIDER_STD, Use standard low quality icons in wxArtProvider.}
 @itemdef{wxUSE_ARTPROVIDER_TANGO, Use Tango icons in wxArtProvider.}
 @itemdef{wxUSE_ANY, Use wxAny class.}
-@itemdef{wxUSE_APPLE_IEEE, IEEE Extended to/from double routines; see src/common/extended.c file.}
+@itemdef{wxUSE_APPLE_IEEE, IEEE Extended to/from double routines, see wxDataOutputStream.}
 @itemdef{wxUSE_ARCHIVE_STREAMS, Enable streams for archive formats.}
 @itemdef{wxUSE_AUI, Use AUI (dockable windows) library.}
 @itemdef{wxUSE_BASE64, Enables Base64 support.}
index 362f3b4c916f8dcb3fdab432d09a4592972ee192..d96482ba34f57d0a3a6821aebaa47a1e7283ad77 100644 (file)
@@ -24,6 +24,28 @@ class WXDLLIMPEXP_BASE wxDataStreamBase
 public:
     void BigEndianOrdered(bool be_order) { m_be_order = be_order; }
 
+    // By default we use extended precision (80 bit) format for both float and
+    // doubles. Call this function to switch to alternative representation in
+    // which IEEE 754 single precision (32 bits) is used for floats and double
+    // precision (64 bits) is used for doubles.
+    void UseBasicPrecisions()
+    {
+#if !wxUSE_APPLE_IEEE
+        m_useExtendedPrecision = false;
+#endif // !wxUSE_APPLE_IEEE
+    }
+
+    // UseExtendedPrecision() is not very useful as it corresponds to the
+    // default value, only call it in your code if you want the compilation
+    // fail with the error when using wxWidgets library compiled without
+    // extended precision support.
+#if wxUSE_APPLE_IEEE
+    void UseExtendedPrecision()
+    {
+        m_useExtendedPrecision = true;
+    }
+#endif // wxUSE_APPLE_IEEE
+
 #if wxUSE_UNICODE
     void SetConv( const wxMBConv &conv );
     wxMBConv *GetConv() const { return m_conv; }
@@ -38,6 +60,10 @@ protected:
 
     bool m_be_order;
 
+#if wxUSE_APPLE_IEEE
+    bool m_useExtendedPrecision;
+#endif // wxUSE_APPLE_IEEE
+
 #if wxUSE_UNICODE
     wxMBConv *m_conv;
 #endif
@@ -63,6 +89,7 @@ public:
     wxUint16 Read16();
     wxUint8 Read8();
     double ReadDouble();
+    float ReadFloat();
     wxString ReadString();
 
 #if wxHAS_INT64
@@ -81,6 +108,7 @@ public:
     void Read16(wxUint16 *buffer, size_t size);
     void Read8(wxUint8 *buffer, size_t size);
     void ReadDouble(double *buffer, size_t size);
+    void ReadFloat(float *buffer, size_t size);
 
     wxDataInputStream& operator>>(wxString& s);
     wxDataInputStream& operator>>(wxInt8& c);
@@ -125,6 +153,7 @@ public:
     void Write16(wxUint16 i);
     void Write8(wxUint8 i);
     void WriteDouble(double d);
+    void WriteFloat(float f);
     void WriteString(const wxString& string);
 
 #if wxHAS_INT64
@@ -143,6 +172,7 @@ public:
     void Write16(const wxUint16 *buffer, size_t size);
     void Write8(const wxUint8 *buffer, size_t size);
     void WriteDouble(const double *buffer, size_t size);
+    void WriteFloat(const float *buffer, size_t size);
 
     wxDataOutputStream& operator<<(const wxString& string);
     wxDataOutputStream& operator<<(wxInt8 c);
index cfba8e250e0f0647d7713b55d81006b0ec596ac7..c4fc3d272ea4122abf99364e32c5d4f28d664a01 100644 (file)
     @class wxDataOutputStream
 
     This class provides functions that write binary data types in a portable
-    way. Data can be written in either big-endian or little-endian format,
-    little-endian being the default on all architectures.
+    way.
+
+    Data can be written in either big-endian or little-endian format,
+    little-endian being the default on all architectures but BigEndianOrdered()
+    can be used to change this. The default format for the floating point types
+    is 80 bit "extended precision" unless @c wxUSE_APPLE_IEEE was turned off
+    during the library compilation, in which case extended precision is not
+    available at all. You can call UseBasicPrecisions() to change this and
+    use the standard IEEE 754 32 bit single precision format for floats and
+    standard 64 bit double precision format for doubles. This is recommended
+    for the new code for better interoperability with other software that
+    typically uses standard IEEE 754 formats for its data, the use of extended
+    precision by default is solely due to backwards compatibility.
 
     If you want to write data to text files (or streams) use wxTextOutputStream
     instead.
@@ -69,6 +80,40 @@ public:
     */
     void SetConv( const wxMBConv &conv );
 
+    /**
+        Disables the use of extended precision format for floating point
+        numbers.
+
+        This method disables the use of 80 bit extended precision format for
+        the @c float and @c double values written to the stream, which is used
+        by default (unless @c wxUSE_APPLE_IEEE was set to @c 0 when building
+        the library, in which case the extended format support is not available
+        at all and this function does nothing).
+
+        After calling it, @c float values will be written out in one of IEEE
+        754 "basic formats", i.e. 32 bit single precision format for floats and
+        64 bit double precision format for doubles.
+
+        @since 2.9.5
+    */
+    void UseBasicPrecisions();
+
+    /**
+        Explicitly request the use of extended precision for floating point
+        numbers.
+
+        This function allows the application code to explicitly request the use
+        of 80 bit extended precision format for the floating point numbers.
+        This is the case by default but using this function explicitly ensures
+        that the compilation of code relying on producing the output stream
+        using extended precision would fail when using a version of wxWidgets
+        compiled with @c wxUSE_APPLE_IEEE==0 and so not supporting this format
+        at all.
+
+        @since 2.9.5
+     */
+    void UseExtendedPrecision();
+
     /**
         Writes the single byte @a i8 to the stream.
     */
@@ -110,9 +155,34 @@ public:
     void Write64(const wxUint64* buffer, size_t size);
 
     /**
-        Writes the double @a d to the stream using the IEEE format.
+        Writes the float @a f to the stream.
+
+        If UseBasicPrecisions() had been called, the value is written out using
+        the standard IEEE 754 32 bit single precision format. Otherwise, this
+        method uses the same format as WriteDouble(), i.e. 80 bit extended
+        precision representation.
+
+        @since 2.9.5
+    */
+    void WriteFloat(float f);
+
+    /**
+        Writes an array of float to the stream. The number of floats to write is
+        specified by the @a size variable.
+
+        @since 2.9.5
+    */
+    void WriteFloat(const float* buffer, size_t size);
+
+    /**
+        Writes the double @a d to the stream.
+
+        The output format is either 80 bit extended precision or, if
+        UseBasicPrecisions() had been called, standard IEEE 754 64 bit double
+        precision.
     */
     void WriteDouble(double d);
+
     /**
         Writes an array of double to the stream. The number of doubles to write is
         specified by the @a size variable.
@@ -140,8 +210,10 @@ public:
     @class wxDataInputStream
 
     This class provides functions that read binary data types in a portable
-    way. Data can be read in either big-endian or little-endian format,
-    little-endian being the default on all architectures.
+    way.
+
+    Please see wxDataOutputStream for the discussion of the format expected by
+    this stream on input, notably for the floating point values.
 
     If you want to read data from text files (or streams) use wxTextInputStream
     instead.
@@ -254,11 +326,37 @@ public:
     void Read64(wxUint64* buffer, size_t size);
 
     /**
-        Reads a double (IEEE encoded) from the stream.
+        Reads a float from the stream.
+
+        Notice that if UseBasicPrecisions() hadn't been called, this function
+        simply reads a double and truncates it to float as by default the same
+        (80 bit extended precision) representation is used for both float and
+        double values.
+
+        @since 2.9.5
+    */
+    float ReadFloat();
+
+    /**
+        Reads float data from the stream in a specified buffer.
+
+        The number of floats to read is specified by the @a size variable.
+
+        @since 2.9.5
+    */
+    void ReadFloat(float* buffer, size_t size);
+
+    /**
+        Reads a double from the stream.
+
+        The expected format is either 80 bit extended precision or, if
+        UseBasicPrecisions() had been called, standard IEEE 754 64 bit double
+        precision.
     */
     double ReadDouble();
+
     /**
-        Reads double data (IEEE encoded) from the stream in a specified buffer.
+        Reads double data  from the stream in a specified buffer.
 
         The number of doubles to read is specified by the @a size variable.
     */
@@ -283,5 +381,39 @@ public:
        Sets the text conversion class used for reading strings.
     */
     void SetConv( const wxMBConv &conv );
+
+    /**
+        Disables the use of extended precision format for floating point
+        numbers.
+
+        This method disables the use of 80 bit extended precision format for
+        the @c float and @c double values read from the stream, which is used
+        by default (unless @c wxUSE_APPLE_IEEE was set to @c 0 when building
+        the library, in which case the extended format support is not available
+        at all and this function does nothing).
+
+        After calling it, @c float values will be expected to appear in one of
+        IEEE 754 "basic formats", i.e. 32 bit single precision format for
+        floats and 64 bit double precision format for doubles in the input.
+
+        @since 2.9.5
+    */
+    void UseBasicPrecisions();
+
+    /**
+        Explicitly request the use of extended precision for floating point
+        numbers.
+
+        This function allows the application code to explicitly request the use
+        of 80 bit extended precision format for the floating point numbers.
+        This is the case by default but using this function explicitly ensures
+        that the compilation of code relying on reading the input containing
+        numbers in extended precision format would fail when using a version of
+        wxWidgets compiled with @c wxUSE_APPLE_IEEE==0 and so not supporting
+        this format at all.
+
+        @since 2.9.5
+     */
+    void UseExtendedPrecision();
 };
 
index 861b46a8d7115062efb6fb089be332cb673b20bd..c55f2134fbc9b703f3b5df0df9ffe2334d7a79aa 100644 (file)
     #include "wx/math.h"
 #endif //WX_PRECOMP
 
+namespace
+{
+
+// helper unions used to swap bytes of floats and doubles
+union Float32Data
+{
+    wxFloat32 f;
+    wxUint32 i;
+};
+
+union Float64Data
+{
+    wxFloat64 f;
+    wxUint32 i[2];
+};
+
+} // anonymous namespace
+
 // ----------------------------------------------------------------------------
 // wxDataStreamBase
 // ----------------------------------------------------------------------------
@@ -37,6 +55,12 @@ wxDataStreamBase::wxDataStreamBase(const wxMBConv& conv)
     wxUnusedVar(conv);
 
     m_be_order = false;
+
+    // For compatibility with the existing data files, we use extended
+    // precision if it is available, i.e. if wxUSE_APPLE_IEEE is on.
+#if wxUSE_APPLE_IEEE
+    m_useExtendedPrecision = true;
+#endif // wxUSE_APPLE_IEEE
 }
 
 #if wxUSE_UNICODE
@@ -108,13 +132,48 @@ wxUint8 wxDataInputStream::Read8()
 double wxDataInputStream::ReadDouble()
 {
 #if wxUSE_APPLE_IEEE
-  char buf[10];
+    if ( m_useExtendedPrecision )
+    {
+        char buf[10];
 
-  m_input->Read(buf, 10);
-  return wxConvertFromIeeeExtended((const wxInt8 *)buf);
-#else
-  return 0.0;
-#endif
+        m_input->Read(buf, 10);
+        return wxConvertFromIeeeExtended((const wxInt8 *)buf);
+    }
+    else
+#endif // wxUSE_APPLE_IEEE
+    {
+        Float64Data floatData;
+
+        if ( m_be_order == (wxBYTE_ORDER == wxBIG_ENDIAN) )
+        {
+            floatData.i[0] = Read32();
+            floatData.i[1] = Read32();
+        }
+        else
+        {
+            floatData.i[1] = Read32();
+            floatData.i[0] = Read32();
+        }
+
+        return static_cast<double>(floatData.f);
+    }
+}
+
+float wxDataInputStream::ReadFloat()
+{
+#if wxUSE_APPLE_IEEE
+    if ( m_useExtendedPrecision )
+    {
+        return (float)ReadDouble();
+    }
+    else
+#endif // wxUSE_APPLE_IEEE
+    {
+        Float32Data floatData;
+
+        floatData.i = Read32();
+        return static_cast<float>(floatData.f);
+    }
 }
 
 wxString wxDataInputStream::ReadString()
@@ -388,6 +447,14 @@ void wxDataInputStream::ReadDouble(double *buffer, size_t size)
   }
 }
 
+void wxDataInputStream::ReadFloat(float *buffer, size_t size)
+{
+  for (wxUint32 i=0; i<size; i++)
+  {
+    *(buffer++) = ReadFloat();
+  }
+}
+
 wxDataInputStream& wxDataInputStream::operator>>(wxString& s)
 {
   s = ReadString();
@@ -466,7 +533,7 @@ wxDataInputStream& wxDataInputStream::operator>>(double& d)
 
 wxDataInputStream& wxDataInputStream::operator>>(float& f)
 {
-  f = (float)ReadDouble();
+  f = ReadFloat();
   return *this;
 }
 
@@ -535,22 +602,49 @@ void wxDataOutputStream::WriteString(const wxString& string)
 
 void wxDataOutputStream::WriteDouble(double d)
 {
-  char buf[10];
+#if wxUSE_APPLE_IEEE
+    if ( m_useExtendedPrecision )
+    {
+        char buf[10];
+
+        wxConvertToIeeeExtended(d, (wxInt8 *)buf);
+        m_output->Write(buf, 10);
+    }
+    else
+#endif // wxUSE_APPLE_IEEE
+    {
+        Float64Data floatData;
 
+        floatData.f = (wxFloat64)d;
+
+        if ( m_be_order == (wxBYTE_ORDER == wxBIG_ENDIAN) )
+        {
+            Write32(floatData.i[0]);
+            Write32(floatData.i[1]);
+        }
+        else
+        {
+            Write32(floatData.i[1]);
+            Write32(floatData.i[0]);
+        }
+    }
+}
+
+void wxDataOutputStream::WriteFloat(float f)
+{
 #if wxUSE_APPLE_IEEE
-  wxConvertToIeeeExtended(d, (wxInt8 *)buf);
-#else
-  wxUnusedVar(d);
-#if !defined(__VMS__) && !defined(__GNUG__)
-#ifdef _MSC_VER
-# pragma message("wxDataOutputStream::WriteDouble() not using IeeeExtended - will not work!")
-#else
-# pragma warning "wxDataOutputStream::WriteDouble() not using IeeeExtended - will not work!"
-#endif
-#endif
-   buf[0] = '\0';
-#endif
-  m_output->Write(buf, 10);
+    if ( m_useExtendedPrecision )
+    {
+        WriteDouble((double)f);
+    }
+    else
+#endif // wxUSE_APPLE_IEEE
+    {
+        Float32Data floatData;
+
+        floatData.f = (wxFloat32)f;
+        Write32(floatData.i);
+    }
 }
 
 #if wxHAS_INT64
@@ -664,6 +758,14 @@ void wxDataOutputStream::WriteDouble(const double *buffer, size_t size)
   }
 }
 
+void wxDataOutputStream::WriteFloat(const float *buffer, size_t size)
+{
+  for (wxUint32 i=0; i<size; i++)
+  {
+    WriteFloat(*(buffer++));
+  }
+}
+
 wxDataOutputStream& wxDataOutputStream::operator<<(const wxString& string)
 {
   WriteString(string);
@@ -742,7 +844,7 @@ wxDataOutputStream& wxDataOutputStream::operator<<(double d)
 
 wxDataOutputStream& wxDataOutputStream::operator<<(float f)
 {
-  WriteDouble((double)f);
+  WriteFloat(f);
   return *this;
 }
 
index 8f48c285cf2542554129eaf928127e24b05241c1..2aed7a3b04dd8b0b4b66608dc633669f463b13cb 100644 (file)
@@ -50,6 +50,17 @@ private:
         CPPUNIT_TEST( PseudoTest_UseBigEndian );
         CPPUNIT_TEST( FloatRW );
         CPPUNIT_TEST( DoubleRW );
+        // Only test standard IEEE 754 formats if we're using IEEE extended
+        // format by default, otherwise the tests above already covered them.
+#if wxUSE_APPLE_IEEE
+        CPPUNIT_TEST( PseudoTest_UseIEEE754 );
+        CPPUNIT_TEST( FloatRW );
+        CPPUNIT_TEST( DoubleRW );
+        // Also retest little endian version with standard formats.
+        CPPUNIT_TEST( PseudoTest_UseLittleEndian );
+        CPPUNIT_TEST( FloatRW );
+        CPPUNIT_TEST( DoubleRW );
+#endif // wxUSE_APPLE_IEEE
     CPPUNIT_TEST_SUITE_END();
 
     wxFloat64 TestFloatRW(wxFloat64 fValue);
@@ -65,8 +76,15 @@ private:
     void NaNRW();
 
     void PseudoTest_UseBigEndian() { ms_useBigEndianFormat = true; }
+    void PseudoTest_UseLittleEndian() { ms_useBigEndianFormat = false; }
+#if wxUSE_APPLE_IEEE
+    void PseudoTest_UseIEEE754() { ms_useIEEE754 = true; }
+#endif // wxUSE_APPLE_IEEE
 
     static bool ms_useBigEndianFormat;
+#if wxUSE_APPLE_IEEE
+    static bool ms_useIEEE754;
+#endif // wxUSE_APPLE_IEEE
 
     DECLARE_NO_COPY_CLASS(DataStreamTestCase)
 };
@@ -78,6 +96,9 @@ CPPUNIT_TEST_SUITE_REGISTRATION( DataStreamTestCase );
 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( DataStreamTestCase, "DataStreamTestCase" );
 
 bool DataStreamTestCase::ms_useBigEndianFormat = false;
+#if wxUSE_APPLE_IEEE
+bool DataStreamTestCase::ms_useIEEE754 = false;
+#endif // wxUSE_APPLE_IEEE
 
 DataStreamTestCase::DataStreamTestCase()
 {
@@ -91,6 +112,11 @@ wxFloat64 DataStreamTestCase::TestFloatRW(wxFloat64 fValue)
         if ( ms_useBigEndianFormat )
             pDataOutput.BigEndianOrdered(true);
 
+#if wxUSE_APPLE_IEEE
+        if ( ms_useIEEE754 )
+            pDataOutput.UseBasicPrecisions();
+#endif // wxUSE_APPLE_IEEE
+
         pDataOutput << fValue;
     }
 
@@ -99,6 +125,11 @@ wxFloat64 DataStreamTestCase::TestFloatRW(wxFloat64 fValue)
     if ( ms_useBigEndianFormat )
         pDataInput.BigEndianOrdered(true);
 
+#if wxUSE_APPLE_IEEE
+    if ( ms_useIEEE754 )
+        pDataInput.UseBasicPrecisions();
+#endif // wxUSE_APPLE_IEEE
+
     wxFloat64 fInFloat;
 
     pDataInput >> fInFloat;