Date: Wednesday, March 22, 2006 @ 19:22:04
  Author: gilles
    Path: /cvsroot/carob/carob

Modified: include/BigDecimal.hpp (1.18 -> 1.19) src/BigDecimal.cpp (1.23
          -> 1.24)

Fixed negative overflow for int64_t cast operator
Added contructors with wstring, string, int, long and int64
Prepared ground for contructors with float and double
Added initToZero() common function for constructors
Introduced toString(wchar_t) that takes the decimal point separator as parameter
(wstring) cast operator now uses this function with the user's locale separator 
got from Common
Fixed << operator to send controller decimal point (got from Common) instead of 
user's one
Added = (assignment) operator
Optimized comparison with 0 (zero): now use mpz_size instead of mpz_cmp_d

Consider as a fix to CAROB-68 (only float and double constructors are missing, 
new JIRA to create)


------------------------+
 include/BigDecimal.hpp |   67 +++++++++++++-
 src/BigDecimal.cpp     |  210 ++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 254 insertions(+), 23 deletions(-)


Index: carob/include/BigDecimal.hpp
diff -u carob/include/BigDecimal.hpp:1.18 carob/include/BigDecimal.hpp:1.19
--- carob/include/BigDecimal.hpp:1.18   Fri Mar 10 22:15:45 2006
+++ carob/include/BigDecimal.hpp        Wed Mar 22 19:22:04 2006
@@ -23,7 +23,7 @@
 #define BIGDECIMAL_H_
 
 #include "CarobException.hpp"
-#include "Common.hpp" //for java_byte and ResultSetDataType
+#include "Common.hpp" //for java_byte, ResultSetDataType and 
controllerDecimalPoint
 
 #include <gmpxx.h>
 #include <string>
@@ -32,7 +32,7 @@
 namespace CarobNS {
 
 class DriverSocket;
-
+class std::locale;
 /**
  * Java math.BigDecimal equivalent class for storing immutable, 
  * arbitrary-precision signed decimal numbers. A BigDecimal consists of an 
@@ -56,14 +56,46 @@
   BigDecimal();
 
   /**
+   * Constructs a BigDecimal which value is parsed from the given wstring 
+   * (converted to a string) using #createFromString() function 
+   * @see #createFromString()
+   */
+  BigDecimal(const std::wstring& s) throw (ConversionException, 
UnexpectedException);
+  /**
+   * Constructs a BigDecimal which value is parsed from the given string using
+   * #createFromString() function.
+   * @see #createFromString()
+   */
+  BigDecimal(const std::string& s) throw (ConversionException, 
UnexpectedException);
+  /**
+   * Constructs a BigDecimal which value is taken from the given argument using
+   * #createFromString() function.
+   * @see #createFromString()
+   */
+  BigDecimal(const int);
+  BigDecimal(const long);
+  BigDecimal(const int64_t);
+  //TODO:BigDecimal(const float);
+  //TODO:BigDecimal(const double);
+  
+  /**
+   * Copies scale sign and unscaled_value
+   */
+  BigDecimal& operator = (const BigDecimal&);
+  
+  /**
    * Compares scale, sign and unscaled_value
    */
   bool operator == (const BigDecimal&);
   /**
-   * BigDecimal display or serialization as a string
+   * BigDecimal display or serialization as a string with default C locale
+   * decimal point. This operator is used to send the bigdecimal as a string to
+   * the controller for parameter statements. The controller uses the default
+   * decimal point separator for parsing, so we have to send it the right one,
+   * which is the default C one.
    */
   friend std::basic_ostream<wchar_t>& operator << 
(std::basic_ostream<wchar_t>& os, 
-      const BigDecimal &bd)  { return os<<(static_cast<std::wstring>(bd)); }
+      const BigDecimal &bd)  { return os<<bd.toString(controllerDecimalPoint); 
}
   /**
    * Constructor to deserialize a BigDecimal from a stream.
    * @param input socket from which to deserializer Big Decimal
@@ -77,7 +109,7 @@
   virtual ~BigDecimal();
 
   /**
-   * Convertion to string.
+   * Convertion to string in the user defined locale (for decimal separator)
    * A leading minus sign is used to indicate sign, and the number of digits to
    * the right of the decimal point is used to indicate scale.
    */ 
@@ -125,6 +157,25 @@
   operator double() const throw (ConversionException);
 protected:
   /**
+   * Initializes this big decimal to 0 (zero).
+   * There will be 0 signum, zero unscaled_value, zero scale, zero byte array
+   * length and byte array pointer set to NULL
+   */
+  void          initToZero();
+  /**
+   * Fills this BigDecimal fields (scale, signum, unscaled_value) by converting
+   * the given string into a BigDecimal, assuming it is base 10.
+   * Finds the scale of the given number by parsing the input string in order 
to
+   * find the decimal point taken from the current locale. The scale is the
+   * number of digits found after the decimal point, ignoring white spaces.
+   * @param str string representing the number in base 10, that can contain
+   * white spaces (ignored).
+   * @throw ConversionException if the given string does not represent a valid
+   * BigDecimal in base 10
+   */
+  void          createFromString(const std::string &str)
+                    throw (ConversionException, UnexpectedException);
+  /**
    * Converts this BigDecimal's absolute value to a GMP integer by scaling
    * its unscaled_value (thus discarding fractional part)
    */
@@ -138,6 +189,12 @@
    * @param intArrayLength size of the returned array
    */
   int*          toIntArray(mpz_class scaled_val, int* intArrayLength) const;
+  /**
+   * Convertion to string with the given decimal point
+   * A leading minus sign is used to indicate sign, and the number of digits to
+   * the right of the decimal point is used to indicate scale.
+   */ 
+  std::wstring  toString(const wchar_t) const;
 private:
   /** Length of the unscaled value java_byte-array */
   size_t        byteArrayLength;
Index: carob/src/BigDecimal.cpp
diff -u carob/src/BigDecimal.cpp:1.23 carob/src/BigDecimal.cpp:1.24
--- carob/src/BigDecimal.cpp:1.23       Mon Mar 13 20:31:04 2006
+++ carob/src/BigDecimal.cpp    Wed Mar 22 19:22:04 2006
@@ -26,6 +26,7 @@
 #include "CarobException.hpp"
 #include "Common.hpp"
 
+#include <limits>
 #include <locale>
 #include <sstream> //for wostringstream
 
@@ -33,13 +34,154 @@
 
 using namespace CarobNS;
 
-BigDecimal::BigDecimal() : unscaled_value(0)
+void BigDecimal::initToZero()
 {
+  unscaled_value = 0;
   byteArrayLength = 0;
   byteArray = NULL;
   scale = 0;
   signum = 0;
 }
+
+BigDecimal::BigDecimal()
+{
+  initToZero();
+}
+
+BigDecimal::BigDecimal(const std::wstring& wstr) throw (ConversionException, 
UnexpectedException)
+{
+  createFromString(StaticCodecs::toString(wstr));
+}
+
+BigDecimal::BigDecimal(const std::string& str) throw (ConversionException, 
UnexpectedException)
+{
+  createFromString(str);
+}
+
+void BigDecimal::createFromString(const std::string& str)
+    throw (ConversionException, UnexpectedException)
+{
+  initToZero();
+
+  std::locale currentLocale = std::locale();
+
+  std::string unscaledStr = str;
+  //TODO: multi-char decimal points
+  char decimalPoint = std::use_facet<std::numpunct<char> 
>(currentLocale).decimal_point();
+  int strSize = str.length();
+  char currentChar = 0;
+  bool decimalPointFound = false;
+  scale  = 0;
+  for (int i=0; i<strSize; i++)
+  {
+    currentChar = unscaledStr[i];
+    if (!isspace(currentChar, currentLocale))
+    {
+      if (currentChar == decimalPoint)
+      {
+        if (decimalPointFound)
+        {
+          //double decimal point => exception
+          throw ConversionException(L"Detected more than one decimal point [" +
+              toWString(decimalPoint) + L"] in BigDecimal string ["+ 
fromString(str) + L"]", CONV_NUM_RANGE);
+        }
+        // remove the decimal point (actually replace it by a space)
+        unscaledStr.replace(i, 1, " ");
+        decimalPointFound = true;
+      }
+      else if (decimalPointFound)
+        scale++;
+    }
+  }
+  if (mpz_set_str(unscaled_value.get_mpz_t(), unscaledStr.c_str(), 10) != 0)
+  {
+    throw ConversionException(fromString(str) + L"does not represent a valid 
BigDecimal value", L"FIXME");
+  }
+  if (unscaled_value < 0)
+  {
+    unscaled_value = -unscaled_value;
+    signum = -1;
+  }
+  else if (unscaled_value == 0)
+    signum = 0;
+  else
+    signum = 1;
+}
+
+BigDecimal::BigDecimal(const int arg)
+{
+  initToZero();
+  if (arg > 0)
+    signum = 1;
+  else if (arg < 0)
+    signum = -1;
+  else
+    return;
+  mpz_set_si(unscaled_value.get_mpz_t(), arg);
+  if (signum<0) //take the abs()
+    mpz_neg(unscaled_value.get_mpz_t(), unscaled_value.get_mpz_t());
+}
+
+BigDecimal::BigDecimal(const long arg)
+{
+  initToZero();
+  if (arg > 0L)
+    signum = 1;
+  else if (arg < 0L)
+    signum = -1;
+  else
+    return;
+  mpz_set_si(unscaled_value.get_mpz_t(), arg);
+  if (signum<0) //take the abs()
+    mpz_neg(unscaled_value.get_mpz_t(), unscaled_value.get_mpz_t());
+}
+
+BigDecimal::BigDecimal(const int64_t arg)
+{
+  initToZero();
+  if (arg > 0)
+    signum = 1;
+  else if (arg < 0)
+    signum = -1;
+  else
+    return;
+  // no setter for long long => import as an array of 1 int64...
+  int64_t import = arg;
+  bool handleMinLL = false;
+  if (signum < 0) //handle sign bit & min int64 correcly
+  {
+    import = -arg;
+    if (arg == std::numeric_limits<int64_t>::min())
+    {
+      // special value, its abs() cannot be represented in an int64
+      // => set to max int64 and put flag so we will add 1 after the import
+      import = std::numeric_limits<int64_t>::max();
+      handleMinLL = true;
+    }
+  }
+  mpz_import (unscaled_value.get_mpz_t(),
+              1,
+              0, // only one "64bits word" to import => order doesn't matter
+              sizeof(int64_t),
+              0, // native endianness
+              0, // sign bit has been removed by the previous mask
+              &import);
+  if (handleMinLL) // special case, see above min int64 handling
+    mpz_add_ui(unscaled_value.get_mpz_t(), unscaled_value.get_mpz_t(), 1);
+}
+
+#if 0
+BigDecimal::BigDecimal(const float arg)
+{
+  //TODO
+}
+
+BigDecimal::BigDecimal(const double)
+{
+  //TODO
+}
+#endif
+
 BigDecimal::BigDecimal(const DriverSocket& input)
     throw (SocketIOException, UnexpectedException) : unscaled_value(0)
 {
@@ -118,6 +260,19 @@
   }
 }
 
+BigDecimal& BigDecimal::operator = (const BigDecimal& ref)
+{
+  signum = ref.signum;
+  scale = ref.scale;
+  unscaled_value = ref.unscaled_value;
+  byteArrayLength = ref.byteArrayLength;
+  if (byteArrayLength != 0)
+  {
+    memcpy(byteArray, ref.byteArray, byteArrayLength*sizeof(byteArray[0]));
+  }
+  return *this;
+}
+
 bool BigDecimal::operator == (const BigDecimal& ref)
 {
   return (signum == ref.signum
@@ -157,9 +312,14 @@
   return mag;
 }
 
-// FIXME: this function can be optimized: lots of front-inserts can be avoided
 BigDecimal::operator wstring() const
 {
+  return toString(userDecimalPoint); //use user-defined locale
+}
+
+// FIXME: this function can be optimized: lots of front-inserts can be avoided
+std::wstring BigDecimal::toString(const wchar_t decimal_point) const
+{
   wstring sRet(L"0");
   if (signum != 0) // 0 signum means 0 value => no useless computations !
   {
@@ -176,7 +336,6 @@
         sRet.insert(0, scale - sRetLength + 1, L'0');
       }
       // insert the decimal point
-      wchar_t decimal_point = std::use_facet<std::numpunct<wchar_t> 
>(std::locale()).decimal_point();
       sRet.insert(sRet.length()-scale, 1, decimal_point);
     }
     // and the optional sign
@@ -191,10 +350,11 @@
 {
   if (signum == 0)
     return 0;
-  mpz_class scaled_int = toBigInteger();
-  if (mpz_cmp_d(scaled_int.get_mpz_t(), 0) == 0) // if scaled_int == 0
+  mpz_class scaled_int = toBigInteger(); // truncate value
+  if (mpz_size(scaled_int.get_mpz_t()) == 0) // ie. if (scaled_int == 0)
     return 0;
-  // Check we can convert to int
+  // Check that we can convert to int
+  // TODO: the same with only one "mpz_fits" and a test on int min
   if (signum>0)
   {
     if (!mpz_fits_sint_p(scaled_int.get_mpz_t()))
@@ -229,7 +389,7 @@
   if (signum == 0)
     return 0;
   mpz_class scaled_int = toBigInteger();
-  if (mpz_cmp_d(scaled_int.get_mpz_t(), 0) == 0) // if scaled_int == 0
+  if (mpz_size(scaled_int.get_mpz_t()) == 0) // ie. if scaled_int == 0
     return 0;
   // Check we can convert to int
   if (signum>0)
@@ -249,24 +409,37 @@
   }
   return (signum > 0 ? scaled_int.get_si() : -scaled_int.get_si());
 }
-
 //TODO: optimize this horribly slow stuff
-//FIXME => negative overflow
 BigDecimal::operator int64_t() const throw (ConversionException)
 {
   if (signum == 0)
     return 0;
   mpz_class scaled_int = toBigInteger();
-  if (mpz_cmp_d(scaled_int.get_mpz_t(), 0) == 0) // if scaled_int == 0
+  if (mpz_size(scaled_int.get_mpz_t()) == 0) // ie. if scaled_int == 0
     return 0;
   // check size
   size_t nbBitsNeeded = mpz_sizeinbase(scaled_int.get_mpz_t(), 2);
-  if (signum > 0)
-    nbBitsNeeded++; // count the sign bit. Negative overflow will be detected 
later (TODO)
-  if (nbBitsNeeded > sizeof(int64_t)*8)
-    throw (ConversionException(L"BigDecimal " + static_cast<wstring>(*this)
-        + L" is too big to be converted into an int64_t", CONV_NUM_RANGE));
-
+  if (nbBitsNeeded+1 > sizeof(int64_t)*8) //+1 for the sign bit
+  {
+    bool numTooBig = true;
+    // Handle special "min int64" value:
+    if (signum < 0 && nbBitsNeeded == sizeof(int64_t)*8)
+    {
+      // min int64 is the only value that requires 64bits and which can be
+      // represented by a signed int64 (which will be 10000...(63 zeros total)
+      // So we have to compare the unscaled_value to abs(numeric_limits<long 
long>::min())
+      // in order to know if we can cast the number
+      // TODO: is there an optimized way to do it ?
+      BigDecimal minInt64(std::numeric_limits<int64_t>::min());
+      if (minInt64 == *this)
+        numTooBig = false;
+    }
+    if (numTooBig)
+    {
+      throw (ConversionException(L"BigDecimal " + static_cast<wstring>(*this)
+          + L" is too big to be converted into an int64_t", CONV_NUM_RANGE));
+    }
+  }
   int mag_length = -1;
   int* mag = toIntArray(scaled_int, &mag_length);
   if (mag_length <= 0) //should not happen, but safer
@@ -274,10 +447,11 @@
     throw (ConversionException(L"BigDecimal " + static_cast<wstring>(*this)
         + L" could not be converted to int64", L"FIXME"));
   }
-
   int64_t result = 0;
   for (int i = 0; i < mag_length; i++)
   {
+    // min int64 special value will do an overflow that magically falls back
+    // to 0x80000000
     result = (result<<(8*sizeof(int))) + (mag[i]&0xffffffffLL);
   }
   delete[] mag;
@@ -295,7 +469,7 @@
     throw (ConversionException(L"BigDecimal " + static_cast<wstring>(*this)
         +L" is negative ! it cannot be converted into an int64_t", 
CONV_NUM_RANGE));
   mpz_class scaled_int = toBigInteger();
-  if (mpz_cmp_d(scaled_int.get_mpz_t(), 0) == 0) // if scaled_int == 0
+  if (mpz_size(scaled_int.get_mpz_t()) == 0) // ie. if scaled_int == 0
     return 0;
   // check size
   size_t nbBitsNeeded = mpz_sizeinbase(scaled_int.get_mpz_t(), 2);

_______________________________________________
Carob-commits mailing list
[email protected]
https://forge.continuent.org/mailman/listinfo/carob-commits

Reply via email to