Date: Wednesday, January 25, 2006 @ 23:59:02
Author: marc
Path: /cvsroot/carob/carob
Modified: include/SQLDataSerialization.hpp (1.9 -> 1.10)
src/SQLDataSerialization.cpp (1.17 -> 1.18)
CAROB-31: reworked deserialization of floats and doubles, should be
really portable now: no more "union casting" + sanity checks on IEEE
754 binary format and endianness at start-up time. Needs testing on
big endian arch.
----------------------------------+
include/SQLDataSerialization.hpp | 29 +++++
src/SQLDataSerialization.cpp | 198 ++++++++++++++++++++++++++++++-------
2 files changed, 192 insertions(+), 35 deletions(-)
Index: carob/include/SQLDataSerialization.hpp
diff -u carob/include/SQLDataSerialization.hpp:1.9
carob/include/SQLDataSerialization.hpp:1.10
--- carob/include/SQLDataSerialization.hpp:1.9 Wed Jan 25 23:10:13 2006
+++ carob/include/SQLDataSerialization.hpp Wed Jan 25 23:59:02 2006
@@ -16,7 +16,7 @@
* limitations under the License.
*
* Initial developer(s): Gilles Rayrat
- * Contributor(s):
+ * Contributor(s): Marc Herbert
*/
#ifndef SQLDATASERIALIZATION_H_
@@ -48,6 +48,33 @@
*/
static deserializerPtr getDeserializer(TypeTag ttPrm)
throw (NotImplementedException, UnexpectedException);
+
+ /**
+ * Relatively safe "reinterpret_cast" of an IEEE 754 float to its
+ * uint32_t bit representation. Does NOT swap bytes in any way, so
+ * endianness of the result is arch-specific. Endianness of floats
+ * is typically the same as for integer types, but not granted.
+ */
+ static uint32_t
+ floatToU32Bits(float f);
+
+ /** See floatToU32Bits(float f) */
+ static float
+ U32BitsToFloat(uint32_t ui);
+
+ /**
+ * Relatively safe "reinterpret_cast" of an IEEE 754 double to its
+ * uint64_t bit representation. Does NOT swap bytes in any way, so
+ * endianness of the result is arch-specific. Endianness of doubles
+ * is typically the same as for integer types, but not granted.
+ */
+ static uint64_t
+ doubleToU64Bits(double f);
+
+ /** See doubleToU64Bits(double d) */
+ static double
+ U64BitsToDouble(uint64_t ui);
+
};
/**
Index: carob/src/SQLDataSerialization.cpp
diff -u carob/src/SQLDataSerialization.cpp:1.17
carob/src/SQLDataSerialization.cpp:1.18
--- carob/src/SQLDataSerialization.cpp:1.17 Wed Jan 25 23:05:25 2006
+++ carob/src/SQLDataSerialization.cpp Wed Jan 25 23:59:02 2006
@@ -16,7 +16,7 @@
* limitations under the License.
*
* Initial developer(s): Gilles Rayrat
- * Contributor(s):
+ * Contributor(s): Marc Herbert
*/
#include "SQLDataSerialization.hpp"
@@ -38,13 +38,22 @@
#ifndef __STDC_IEC_559__
#error only IEEE 754 platforms are supported
#endif
- // also available at run-time: numeric_limits<double|float>.is_iec559()
+ // run-time equivalent is: numeric_limits<double|float>.is_iec559()
+#if (CHAR_BIT != 8)
+#error CHAR_BIT != 8 is not supported
+#endif
using std::wstring;
using namespace CarobNS;
+namespace {
+ // forward
+ extern const bool floats_inverted_endianness;
+}
+
+
// String
ResultSetDataType stringDeserializer(const DriverSocket& input)
throw (SocketIOException, UnexpectedException)
@@ -93,6 +102,27 @@
}
// Float
+
+uint32_t
+SQLDataSerialization::floatToU32Bits(float f)
+{
+ const unsigned char *bytes = reinterpret_cast<const unsigned char *>(&f);
+ uint32_t ires = 0;
+ for (int c=0, shift=0; c<4; c++, shift+=8)
+ ires |= (uint32_t) bytes[c] << shift;
+ return ires;
+}
+
+float
+SQLDataSerialization::U32BitsToFloat(const uint32_t ui)
+{
+ float res;
+ unsigned char *bytes = reinterpret_cast<unsigned char *>(&res);
+ for (int c=0, shift=0; c<4; c++, shift+=8)
+ bytes[c] = ui >> shift;
+ return res;
+}
+
/**
* Converts the input integer which is in in IEEE 754 floating-point
* "single format" bit layout to the corresponding float.
@@ -105,28 +135,45 @@
throw (SocketIOException, UnexpectedException)
{
ResultSetDataType res;
+ int32_t intRead;
-#if 1
- input >> res.as_int;
-#else
-
- int32_t intRead = 0;
-
- //Receive the float as an integer (intbits)
- input>>intRead;
-// OTHER IMPLEM: // buggy? see CAROB-31
- int s = ((intRead & 0x80000000) == 0) ? 1 : -1;
- int e = ((intRead & 0x7f800000) >> 23);
- int m = (intRead & 0x007fffff);
- m |= 0x00800000;
- res.as_float = (float)s * (float)m * (float)pow(2, e-1075);
-
-#endif
+ input >> intRead; // this does call ntohl()
+
+ // "union casting" { float; uint32_t; } is not allowed because of aliasing
rules
+ // alignement issues, etc.
+ // See discussion thread "reinterpret_cast to extract bits" thread in
comp.lang.c++
+ // in may 2004
+ //
http://groups.google.fr/group/comp.std.c++/browse_frm/thread/272f1199322af93d/7d3368013656d923
+ if (floats_inverted_endianness)
+ // obviously cannot use ntohl() to swap unconditionnally!
+ throw NotImplementedException(L"Inverted endianness Not Implemented
Yet");
+ else
+ res.as_float = SQLDataSerialization::U32BitsToFloat(intRead); // sign
casting here
return res;
}
// Double
+uint64_t
+SQLDataSerialization::doubleToU64Bits(double d)
+{
+ uint64_t ires = 0;
+ const unsigned char *bytes = reinterpret_cast<const unsigned char *>(&d);
+ for (int c=0, shift=0; c<8; c++, shift+=8)
+ ires |= (uint64_t) bytes[c] << shift;
+ return ires;
+}
+
+double
+SQLDataSerialization::U64BitsToDouble(const uint64_t ui)
+{
+ double res;
+ unsigned char *bytes = reinterpret_cast<unsigned char *>(&res);
+ for (int c=0, shift=0; c<8; c++, shift+=8)
+ bytes[c] = ui >> shift;
+ return res;
+}
+
/**
* Converts the argument in IEEE 754 floating-point "double format" bit layout
* to the corresponding float. Bit 63 (the most significant) is the sign bit,
@@ -137,24 +184,17 @@
ResultSetDataType doubleDeserializer(const DriverSocket& input)
throw (SocketIOException, UnexpectedException)
{
+ // code duplicated with floatDeserializer (sorry, no template)
+ // -> go and see the numerous implementation comments there
ResultSetDataType res;
+ int64_t intRead;
-#if 1 // see CAROB-31
- input >> res.as_long;
-#else
-
- int64_t longRead = 0;
-
- //Receive the float as an integer (intbits)
- input>>longRead;
-
- long s = ((longRead & 0x8000000000000000LL) == 0) ? 1 : -1;
- long e = ((longRead & 0x7ff0000000000000LL) >> 23);
- long m = (longRead & 0x000fffffffffffffLL);
- m |= 0x0010000000000000LL;
- res.as_double = (double)s * (double)m * (double)pow(2, e-1075);
+ input >> intRead; // this does call ntohll()
-#endif
+ if (floats_inverted_endianness)
+ throw NotImplementedException(L"Inverted endianness Not Implemented
Yet");
+ else
+ res.as_double = SQLDataSerialization::U64BitsToDouble(intRead); // sign
casting here
return res;
}
@@ -274,10 +314,100 @@
}
}
+namespace {
+
+// shortcuts
+#define F2I(f) SQLDataSerialization::floatToU32Bits(f)
+#define D2I(d) SQLDataSerialization::doubleToU64Bits(d);
+
+bool
+floats_little_endianness()
+{
+
+ // build some magical float constants
+
+ // e125f = 2^-125, so we have: s = 0, e = 2, m = 0 (first '1' in m
+ // is implicit). So that's just one bit set to '1' in the higher
+ // (s/e) byte, and all the rest is '0'
+ float e125f_ = 1;
+ for (int i=0; i < 5; i++)
+ e125f_ /= (1 << 25);
+ // insert a '1' in the digit before the last
+ float e125fbis_ = e125f_ + e125f_ / (1 << 22);
+
+ uint32_t e125f = F2I(e125f_);
+ uint32_t e125fbis = F2I(e125fbis_);
+
+
+ // build some magical double constants
+
+ // e1007d = 2^-1007, and same conclusion as e125f above
+ double e1007d_ = 1;
+ for (int i=0; i < 53; i++)
+ e1007d_ /= (1 << 19);
+ // insert a '1' in the digit before the last
+ double e1007dbis_ = e1007d_ + e1007d_ / ((uint64_t) 1 << 51);
+
+ uint64_t e1007d = D2I(e1007d_);
+ uint64_t e1007dbis = D2I(e1007dbis_);
+
+ // then check if we have an classical MSB-float arch
+ // (sign, exponent, fraction)
+ // UNTESTED
+ if (reinterpret_cast<unsigned char*>(&e125f)[0] == 1
+ && reinterpret_cast<unsigned char*>(&e125fbis)[3] == 2
+ && reinterpret_cast<unsigned char*>(&e1007d)[0] == 1
+ && reinterpret_cast<unsigned char*>(&e1007dbis)[7] == 2)
+ return false;
+
+ // else check if we have an classical LSB-float arch
+ // TESTED (on IA32)
+ if (reinterpret_cast<unsigned char*>(&e125f)[3] == 1
+ && reinterpret_cast<unsigned char*>(&e125fbis)[0] == 2
+ && reinterpret_cast<unsigned char*>(&e1007d)[7] == 1
+ && reinterpret_cast<unsigned char*>(&e1007dbis)[0] == 2)
+ return true;
+
+ // else weird unsupported arch, abort
+ throw NotImplementedException(L"Architecture using an unsupported floating
point endianness");
+}
+
+bool
+ints_little_endianness()
+{
+ uint32_t i = 0x01000002U;
+ uint64_t l = 0x0100000000000002ULL;
+
+ // then check if we have an classical MSB-float arch
+ // (sign, exponent, fraction)
+ // UNTESTED
+ if (reinterpret_cast<unsigned char*>(&i)[0] == 1
+ && reinterpret_cast<unsigned char*>(&i)[3] == 2
+ && reinterpret_cast<unsigned char*>(&l)[0] == 1
+ && reinterpret_cast<unsigned char*>(&l)[7] == 2)
+ return false;
+
+ // else check if we have an classical LSB-float arch
+ // TESTED (on IA32)
+ if (reinterpret_cast<unsigned char*>(&i)[3] == 1
+ && reinterpret_cast<unsigned char*>(&i)[0] == 2
+ && reinterpret_cast<unsigned char*>(&l)[7] == 1
+ && reinterpret_cast<unsigned char*>(&l)[0] == 2)
+ return true;
+
+ // else weird unsupported arch, abort
+ throw NotImplementedException(L"Architecture using an unsupported integer
endianness");
+}
+
+const bool floats_inverted_endianness = floats_little_endianness() ^
ints_little_endianness();
+
+} // unnamed namespace
+
+
/*
* Local Variables:
* c-file-style: "bsd"
- * c-basic-offset: 2
+ * c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/
_______________________________________________
Carob-commits mailing list
[email protected]
https://forge.continuent.org/mailman/listinfo/carob-commits