diff --git a/docs/changes.txt b/docs/changes.txt index 2dec057820..c8f9466d11 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -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): diff --git a/docs/doxygen/mainpages/const_wxusedef.h b/docs/doxygen/mainpages/const_wxusedef.h index 4127c8c714..8feb863c1e 100644 --- a/docs/doxygen/mainpages/const_wxusedef.h +++ b/docs/doxygen/mainpages/const_wxusedef.h @@ -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.} diff --git a/include/wx/datstrm.h b/include/wx/datstrm.h index 362f3b4c91..d96482ba34 100644 --- a/include/wx/datstrm.h +++ b/include/wx/datstrm.h @@ -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); diff --git a/interface/wx/datstrm.h b/interface/wx/datstrm.h index cfba8e250e..c4fc3d272e 100644 --- a/interface/wx/datstrm.h +++ b/interface/wx/datstrm.h @@ -10,8 +10,19 @@ @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(); }; diff --git a/src/common/datstrm.cpp b/src/common/datstrm.cpp index 861b46a8d7..c55f2134fb 100644 --- a/src/common/datstrm.cpp +++ b/src/common/datstrm.cpp @@ -24,6 +24,24 @@ #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(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(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>(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 - 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 ) + { + 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 + 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> fInFloat;