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