1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2025-01-19 03:57:14 +01:00

Initial untested implementation of the Decimal type.

This commit is contained in:
Sandu Liviu Catalin 2016-06-05 03:54:15 +03:00
parent 07b8a8b4a0
commit 17e78b83dd
3 changed files with 605 additions and 0 deletions

View File

@ -6,6 +6,7 @@ namespace SqMod {
// ------------------------------------------------------------------------------------------------
extern void Register_LongInt(HSQUIRRELVM vm);
extern void Register_Decimal(HSQUIRRELVM vm);
extern void Register_Math(HSQUIRRELVM vm);
extern void Register_Random(HSQUIRRELVM vm);
@ -13,6 +14,7 @@ extern void Register_Random(HSQUIRRELVM vm);
void Register_Numeric(HSQUIRRELVM vm)
{
Register_LongInt(vm);
Register_Decimal(vm);
Register_Math(vm);
Register_Random(vm);
}

View File

@ -0,0 +1,286 @@
// ------------------------------------------------------------------------------------------------
#include "Library/Numeric/Decimal.hpp"
#include "Library/Numeric/LongInt.hpp"
// ------------------------------------------------------------------------------------------------
#include <cstdio>
#include <cctype>
#include <cstring>
#include <cstdlib>
// ------------------------------------------------------------------------------------------------
namespace SqMod {
// ------------------------------------------------------------------------------------------------
const Int64 Decimal::s_Factors[] = {
static_cast< Int64 >(std::pow(10, 0)),
static_cast< Int64 >(std::pow(10, 1)),
static_cast< Int64 >(std::pow(10, 2)),
static_cast< Int64 >(std::pow(10, 3)),
static_cast< Int64 >(std::pow(10, 4)),
static_cast< Int64 >(std::pow(10, 5)),
static_cast< Int64 >(std::pow(10, 6)),
static_cast< Int64 >(std::pow(10, 7)),
static_cast< Int64 >(std::pow(10, 8)),
static_cast< Int64 >(std::pow(10, 9)),
static_cast< Int64 >(std::pow(10, 10)),
static_cast< Int64 >(std::pow(10, 11)),
static_cast< Int64 >(std::pow(10, 12)),
static_cast< Int64 >(std::pow(10, 13)),
static_cast< Int64 >(std::pow(10, 14)),
static_cast< Int64 >(std::pow(10, 15)),
static_cast< Int64 >(std::pow(10, 16)),
static_cast< Int64 >(std::pow(10, 17)),
static_cast< Int64 >(std::pow(10, 18)),
static_cast< Int64 >(std::pow(10, 19)),
static_cast< Int64 >(std::pow(10, 20)),
static_cast< Int64 >(std::pow(10, 21)),
static_cast< Int64 >(std::pow(10, 22)),
static_cast< Int64 >(std::pow(10, 23)),
static_cast< Int64 >(std::pow(10, 24)),
static_cast< Int64 >(std::pow(10, 25)),
static_cast< Int64 >(std::pow(10, 26)),
static_cast< Int64 >(std::pow(10, 27)),
static_cast< Int64 >(std::pow(10, 28)),
static_cast< Int64 >(std::pow(10, 29)),
static_cast< Int64 >(std::pow(10, 30)),
static_cast< Int64 >(std::pow(10, 31)),
static_cast< Int64 >(std::pow(10, 32)),
static_cast< Int64 >(std::pow(10, 33)),
static_cast< Int64 >(std::pow(10, 34)),
static_cast< Int64 >(std::pow(10, 35)),
static_cast< Int64 >(std::pow(10, 36)),
static_cast< Int64 >(std::pow(10, 37)),
static_cast< Int64 >(std::pow(10, 38)),
static_cast< Int64 >(std::pow(10, 39)),
static_cast< Int64 >(std::pow(10, 40)),
static_cast< Int64 >(std::pow(10, 41)),
static_cast< Int64 >(std::pow(10, 42)),
static_cast< Int64 >(std::pow(10, 43)),
static_cast< Int64 >(std::pow(10, 44)),
static_cast< Int64 >(std::pow(10, 45)),
static_cast< Int64 >(std::pow(10, 46)),
static_cast< Int64 >(std::pow(10, 47)),
static_cast< Int64 >(std::pow(10, 48)),
static_cast< Int64 >(std::pow(10, 49)),
static_cast< Int64 >(std::pow(10, 40)),
static_cast< Int64 >(std::pow(10, 51)),
static_cast< Int64 >(std::pow(10, 52)),
static_cast< Int64 >(std::pow(10, 53)),
static_cast< Int64 >(std::pow(10, 54)),
static_cast< Int64 >(std::pow(10, 55)),
static_cast< Int64 >(std::pow(10, 56)),
static_cast< Int64 >(std::pow(10, 57)),
static_cast< Int64 >(std::pow(10, 58)),
static_cast< Int64 >(std::pow(10, 59)),
static_cast< Int64 >(std::pow(10, 60)),
static_cast< Int64 >(std::pow(10, 61)),
static_cast< Int64 >(std::pow(10, 62)),
static_cast< Int64 >(std::pow(10, 63)),
};
// ------------------------------------------------------------------------------------------------
SQInteger Decimal::Typename(HSQUIRRELVM vm)
{
static const SQChar name[] = _SC("SqDecimal");
sq_pushstring(vm, name, sizeof(name));
return 1;
}
// ------------------------------------------------------------------------------------------------
void Decimal::ValidatePrecision(Uint8 precision)
{
if (precision >= (sizeof(s_Factors) / sizeof(Int64)))
{
STHROWF("Out of range precision: %u >= %u", precision, (sizeof(s_Factors) / sizeof(Int64)));
}
}
// ------------------------------------------------------------------------------------------------
Int64 Decimal::Convert(const Decimal & dec) const
{
// Do they already have the same precision
if (m_Precision == dec.m_Precision)
{
return dec.m_Value;
}
// Return the value converted to the new precision
return (dec.m_Value / s_Factors[dec.m_Precision]) * s_Factors[m_Precision];
}
// ------------------------------------------------------------------------------------------------
Int32 Decimal::Compare(const Decimal & o) const
{
// Convert ti the same precision
const Int64 b = Convert(o);
// Perform the comparison
if (m_Value == b)
{
return 0;
}
else if (m_Value > b)
{
return 1;
}
else
{
return -1;
}
}
// ------------------------------------------------------------------------------------------------
Decimal::Decimal(CSStr value)
: m_Value(0), m_Precision(0)
{
// Is there anything to parse at least?
if (!value)
{
STHROWF("Invalid decimal string: null");
}
// Skip whitespace characters
while (std::isspace(*value))
{
++value;
}
// Do we still have anything left to parse?
if (*value == '\0')
{
return; // Leave default values
}
// Is there anything before the separator?
if (*value == '.')
{
// Extract the amount of precision required
m_Precision = std::strlen(++value);
// Extract the value as is
m_Value = std::strtoul(value, nullptr, 10);
// Leave the remaining value to default
return;
}
// Find the fraction separator
CSStr point = std::strchr(value, '.');
// Was there a fraction separator found?
if (!point)
{
// Extract the value as is
m_Value = std::strtoul(value, nullptr, 10);
// Leave the remaining value to default
return;
}
// Extract the amount of precision required
m_Precision = ConvTo< Uint8 >::From(std::strlen(++point));
// Validate the obtained precision
ValidatePrecision(m_Precision);
// Copy the value upto the the separator
std::strncpy(GetTempBuff(), value, point - value - 1)[point - value - 1] = '\0';
// Extract the value itself
m_Value = (std::strtoul(GetTempBuff(), nullptr, 10) * s_Factors[m_Precision]);
m_Value += std::strtoul(point, nullptr, 10);
}
// ------------------------------------------------------------------------------------------------
Decimal::Decimal(SQInteger value, Uint8 precision)
: m_Value(0), m_Precision(precision)
{
// Validate the specified precision
ValidatePrecision(m_Precision);
// Assign the specified value
m_Value = (s_Factors[m_Precision] * value);
}
#ifndef _SQ64
// ------------------------------------------------------------------------------------------------
Decimal::Decimal(Int64 value, Uint8 precision)
: m_Value(0), m_Precision(precision)
{
// Validate the specified precision
ValidatePrecision(m_Precision);
// Assign the specified value
m_Value = (s_Factors[m_Precision] * value);
}
#endif // _SQ64
// ------------------------------------------------------------------------------------------------
CSStr Decimal::ToString() const
{
// Generate the string into the temporary buffer
MakeString(GetTempBuff(), GetTempBuffSize());
// Return the resulted string
return GetTempBuff();
}
// ------------------------------------------------------------------------------------------------
SLongInt Decimal::GetConverted(const Decimal & dec) const
{
return SLongInt(Convert(dec));
}
// ------------------------------------------------------------------------------------------------
SLongInt Decimal::GetFactor() const
{
return SLongInt(s_Factors[m_Precision]);
}
// ------------------------------------------------------------------------------------------------
SLongInt Decimal::GetUnbiased() const
{
return SLongInt(m_Value);
}
// ------------------------------------------------------------------------------------------------
void Decimal::MakeString(CStr buffer, Uint32 size) const
{
const Int64 after = m_Value % s_Factors[m_Precision];
const Int64 before = (m_Value - after) / s_Factors[m_Precision];
// Generate the format string
if (std::snprintf(buffer, size, "%lld.%0*lld", before, m_Precision, after) < 0)
{
buffer[0] = '\0'; // At least make sure the string is null terminated
}
}
// ------------------------------------------------------------------------------------------------
static Decimal Sq_DecimalFromLong(const SLongInt & value, Uint8 precision)
{
return Decimal(value.GetNum(), precision);
}
// ================================================================================================
void Register_Decimal(HSQUIRRELVM vm)
{
RootTable(vm).Bind(_SC("SqDecimal"), Class< Decimal >(vm, _SC("SqDecimal"))
// Constructors
.Ctor()
.Ctor< CSStr >()
.Ctor< SQInteger, Uint8 >()
// Core Meta-methods
.Func(_SC("_tostring"), &Decimal::ToString)
.SquirrelFunc(_SC("_typename"), &Decimal::Typename)
.Func(_SC("_cmp"), &Decimal::Cmp)
// Core Functions
.Func(_SC("tofloat"), &Decimal::GetSqFloat)
// Meta-methods
.Func< Decimal (Decimal::*)(const Decimal &) const >(_SC("_add"), &Decimal::operator +)
.Func< Decimal (Decimal::*)(const Decimal &) const >(_SC("_sub"), &Decimal::operator -)
.Func< Decimal (Decimal::*)(const Decimal &) const >(_SC("_mul"), &Decimal::operator *)
.Func< Decimal (Decimal::*)(const Decimal &) const >(_SC("_div"), &Decimal::operator /)
// Properties
.Prop(_SC("Factor"), &Decimal::GetFactor)
.Prop(_SC("Unbiased"), &Decimal::GetUnbiased)
.Prop(_SC("Precision"), &Decimal::GetPrecision)
.Prop(_SC("Abs"), &Decimal::GetAbs)
.Prop(_SC("Float"), &Decimal::GetSqFloat)
.Prop(_SC("Float32"), &Decimal::GetFloat32)
.Prop(_SC("Float64"), &Decimal::GetFloat64)
.Prop(_SC("Str"), &Decimal::ToString)
// Functions
.Func(_SC("Convert"), &Decimal::GetConverted)
// Static Functions
.StaticFunc(_SC("Long"), &Sq_DecimalFromLong)
.StaticFunc(_SC("FromLong"), &Sq_DecimalFromLong)
);
}
} // Namespace:: SqMod

View File

@ -0,0 +1,317 @@
#ifndef _LIBRARY_NUMERIC_DECIMAL_HPP_
#define _LIBRARY_NUMERIC_DECIMAL_HPP_
// ------------------------------------------------------------------------------------------------
#include "Base/Utility.hpp"
// ------------------------------------------------------------------------------------------------
namespace SqMod {
/* ------------------------------------------------------------------------------------------------
* Simple decimal data type support. Mostly for working with databases that support this type.
*/
class Decimal
{
private:
// --------------------------------------------------------------------------------------------
Int64 m_Value; // Decimal value
Uint8 m_Precision; // Decimal precision
// --------------------------------------------------------------------------------------------
static const Int64 s_Factors[];
protected:
/* --------------------------------------------------------------------------------------------
* Validate the given precision.
*/
static void ValidatePrecision(Uint8 precision);
/* --------------------------------------------------------------------------------------------
* Convert a decimal to the same precision as this one.
*/
Int64 Convert(const Decimal & dec) const;
/* --------------------------------------------------------------------------------------------
* Validate against given decimal.
*/
Int32 Compare(const Decimal & o) const;
/* --------------------------------------------------------------------------------------------
* Result = (a * b) / d
*/
static Int64 MultiplyDivide(Int64 a, Int64 b, Int64 d)
{
if ((std::abs(a) <= std::numeric_limits< Int32 >::max()) ||
(std::abs(b) <= std::numeric_limits< Int32 >::max()))
{
return std::llround(static_cast< Float64 >(a * b) / static_cast< Float64 >(d));
}
return std::llround(static_cast< Float64 >(a) * static_cast< Float64 >(b) / static_cast< Float64 >(d));
}
public:
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
Decimal()
: m_Value(0), m_Precision(4)
{
/* ... */
}
/* --------------------------------------------------------------------------------------------
* String constructor.
*/
Decimal(CSStr value);
/* --------------------------------------------------------------------------------------------
* Numeric constructor.
*/
Decimal(SQInteger value, Uint8 precision);
#ifndef _SQ64
/* --------------------------------------------------------------------------------------------
* Numeric constructor.
*/
Decimal(Int64 value, Uint8 precision);
#endif // _SQ64
/* --------------------------------------------------------------------------------------------
* Copy constructor.
*/
Decimal(const Decimal & o) = default;
/* --------------------------------------------------------------------------------------------
* Move constructor.
*/
Decimal(Decimal && o) = default;
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~Decimal() = default;
/* --------------------------------------------------------------------------------------------
* Copy assignment operator.
*/
Decimal & operator = (const Decimal & o)
{
m_Value = Convert(o);
return *this;
}
/* --------------------------------------------------------------------------------------------
* Move assignment operator.
*/
Decimal & operator = (Decimal && o) = default;
/* --------------------------------------------------------------------------------------------
* Used by the script engine to compare two instances of this type.
*/
Int32 Cmp(const Decimal & o) const
{
return Compare(o);
}
/* --------------------------------------------------------------------------------------------
* Used by the script engine to convert an instance of this type to a string.
*/
CSStr ToString() const;
/* --------------------------------------------------------------------------------------------
* Used by the script engine to retrieve the name from instances of this type.
*/
static SQInteger Typename(HSQUIRRELVM vm);
/* --------------------------------------------------------------------------------------------
* Perform an equality comparison between to instances of this type.
*/
bool operator == (const Decimal & o) const
{
return m_Value == Convert(o);
}
/* --------------------------------------------------------------------------------------------
* Perform an inequality comparison between to instances of this type.
*/
bool operator != (const Decimal & o) const
{
return m_Value != Convert(o);
}
/* --------------------------------------------------------------------------------------------
* Perform a less than comparison between to instances of this type.
*/
bool operator < (const Decimal & o) const
{
return m_Value < Convert(o);
}
/* --------------------------------------------------------------------------------------------
* Perform a greater than comparison between to instances of this type.
*/
bool operator > (const Decimal & o) const
{
return m_Value > Convert(o);
}
/* --------------------------------------------------------------------------------------------
* Perform a less than or equal comparison between to instances of this type.
*/
bool operator <= (const Decimal & o) const
{
return m_Value <= Convert(o);
}
/* --------------------------------------------------------------------------------------------
* Perform a greater than or equal comparison between to instances of this type.
*/
bool operator >= (const Decimal & o) const
{
return m_Value >= Convert(o);
}
/* --------------------------------------------------------------------------------------------
* Perform an addition between to instances of this type.
*/
Decimal operator + (const Decimal & o) const
{
return Decimal(m_Value + Convert(o), m_Precision);
}
/* --------------------------------------------------------------------------------------------
* Perform an subtraction between to instances of this type.
*/
Decimal operator - (const Decimal & o) const
{
return Decimal(m_Value - Convert(o), m_Precision);
}
/* --------------------------------------------------------------------------------------------
* Perform an multiplication between to instances of this type.
*/
Decimal operator * (const Decimal & o) const
{
return Decimal(MultiplyDivide(m_Value, Convert(o), s_Factors[m_Precision]), m_Precision);
}
/* --------------------------------------------------------------------------------------------
* Perform an division between to instances of this type.
*/
Decimal operator / (const Decimal & o) const
{
return Decimal(MultiplyDivide(m_Value, s_Factors[m_Precision], Convert(o)), m_Precision);
}
/* --------------------------------------------------------------------------------------------
* Perform an assignment addition between to instances of this type.
*/
Decimal & operator += (const Decimal & o)
{
m_Value += Convert(o);
return *this;
}
/* --------------------------------------------------------------------------------------------
* Perform an subtraction addition between to instances of this type.
*/
Decimal & operator -= (const Decimal & o)
{
m_Value -= Convert(o);
return *this;
}
/* --------------------------------------------------------------------------------------------
* Perform an multiplication addition between to instances of this type.
*/
Decimal & operator *= (const Decimal & o)
{
m_Value -= MultiplyDivide(m_Value, Convert(o), s_Factors[m_Precision]);
return *this;
}
/* --------------------------------------------------------------------------------------------
* Perform an division addition between to instances of this type.
*/
Decimal & operator /= (const Decimal & o)
{
m_Value -= MultiplyDivide(m_Value, s_Factors[m_Precision], Convert(o));
return *this;
}
/* --------------------------------------------------------------------------------------------
* Convert a decimal to the same precision as this one.
*/
SLongInt GetConverted(const Decimal & dec) const;
/* --------------------------------------------------------------------------------------------
* Retrieve the factor.
*/
SLongInt GetFactor() const;
/* --------------------------------------------------------------------------------------------
* Returns integer value = real_value * (10 ^ precision)
*/
SLongInt GetUnbiased() const;
/* --------------------------------------------------------------------------------------------
* Retrieve the precision.
*/
Uint8 GetPrecision() const
{
return m_Precision;
}
/* --------------------------------------------------------------------------------------------
* Retrieve the absolute value.
*/
Decimal GetAbs() const
{
if (m_Value >= 0)
{
return *this;
}
// Calculate the absolute value
return Decimal(0, s_Factors[m_Precision]) - *this;
}
/* --------------------------------------------------------------------------------------------
* Retrieve the decimal value as a native script floating point value.
*/
SQFloat GetSqFloat() const
{
return static_cast< SQFloat >(m_Value) / static_cast< SQFloat >(m_Precision * s_Factors[m_Precision]);
}
/* --------------------------------------------------------------------------------------------
* Retrieve the decimal value as a single floating point value.
*/
Float32 GetFloat32() const
{
return static_cast< Float32 >(m_Value) / static_cast< Float32 >(m_Precision * s_Factors[m_Precision]);
}
/* --------------------------------------------------------------------------------------------
* Retrieve the decimal value as a double floating point value.
*/
Float64 GetFloat64() const
{
return static_cast< Float64 >(m_Value) / static_cast< Float64 >(m_Precision * s_Factors[m_Precision]);
}
/* --------------------------------------------------------------------------------------------
* Convert the decimal to a string and store it into the given buffer.
*/
void MakeString(CStr buffer, Uint32 size) const;
};
} // Namespace:: SqMod
#endif // _LIBRARY_NUMERIC_DECIMAL_HPP_