From b55ae3431aedd66be04c888a3b1ec1788cd1d650 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Wed, 15 Oct 2014 17:00:49 -0400 Subject: [PATCH] adding real 128 for fixed point 64.64 math --- CMakeLists.txt | 1 + include/fc/real128.hpp | 36 +++++++++++++ src/real128.cpp | 115 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 include/fc/real128.hpp create mode 100644 src/real128.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ec8d33b..efd92be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,6 +92,7 @@ option( UNITY_BUILD OFF ) set( fc_sources src/uint128.cpp + src/real128.cpp src/variant.cpp src/exception.cpp src/variant_object.cpp diff --git a/include/fc/real128.hpp b/include/fc/real128.hpp new file mode 100644 index 0000000..ed8aac3 --- /dev/null +++ b/include/fc/real128.hpp @@ -0,0 +1,36 @@ +#include + +namespace fc { + class variant; + + /** + * Provides 64.64 fixed point math operations + * based upon base 2^64-1 + */ + class real128 + { + public: + real128( uint64_t integer = 0):fixed(integer){} + real128( const std::string& str ); + operator std::string()const; + + friend real128 operator * ( real128 a, const real128& b ) { a *= b; return a; } + friend real128 operator / ( real128 a, const real128& b ) { a /= b; return a; } + friend real128 operator + ( real128 a, const real128& b ) { a += b; return a; } + friend real128 operator - ( real128 a, const real128& b ) { a -= b; return a; } + + real128& operator += ( const real128& o ); + real128& operator -= ( const real128& o ); + real128& operator /= ( const real128& o ); + real128& operator *= ( const real128& o ); + + uint64_t to_uint64()const{ return fixed.high_bits(); } + + private: + uint128 fixed; + }; + + void to_variant( const real128& var, variant& vo ); + void from_variant( const variant& var, real128& vo ); + +} // namespace fc diff --git a/src/real128.cpp b/src/real128.cpp new file mode 100644 index 0000000..a67d0a4 --- /dev/null +++ b/src/real128.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include + +namespace fc +{ + real128& real128::operator += ( const real128& o ) + { + fixed += o.fixed; + return *this; + } + real128& real128::operator -= ( const real128& o ) + { + fixed -= o.fixed; + return *this; + } + + real128& real128::operator /= ( const real128& o ) + { try { + FC_ASSERT( o.fixed > uint128(0), "Divide by Zero" ); + + fc::bigint self(fixed); + fc::bigint other(o.fixed); + self *= fc::bigint(uint128(0,-1)); + self /= other; + fixed = self; + + return *this; + } FC_CAPTURE_AND_RETHROW( (*this)(o) ) } + + real128& real128::operator *= ( const real128& o ) + { try { + fc::bigint self(fixed); + fc::bigint other(o.fixed); + self *= other; + self /= fc::bigint(uint128(0,-1)); + fixed = self; + return *this; + } FC_CAPTURE_AND_RETHROW( (*this)(o) ) } + + + real128::real128( const std::string& ratio_str ) + { + const char* c = ratio_str.c_str(); + int digit = *c - '0'; + if (digit >= 0 && digit <= 9) + { + uint64_t int_part = digit; + ++c; + digit = *c - '0'; + while (digit >= 0 && digit <= 9) + { + int_part = int_part * 10 + digit; + ++c; + digit = *c - '0'; + } + fixed = fc::uint128(int_part); + } + else + { + // if the string doesn't look like "123.45" or ".45", this code isn't designed to parse it correctly + // in particular, we don't try to handle leading whitespace or '+'/'-' indicators at the beginning + assert(*c == '.'); + fixed = fc::uint128(); + } + + + if (*c == '.') + { + c++; + digit = *c - '0'; + if (digit >= 0 && digit <= 9) + { + int64_t frac_part = digit; + int64_t frac_magnitude = 10; + ++c; + digit = *c - '0'; + while (digit >= 0 && digit <= 9) + { + frac_part = frac_part * 10 + digit; + frac_magnitude *= 10; + ++c; + digit = *c - '0'; + } + *this += real128( frac_part ) / real128( frac_magnitude ); + } + } + } + real128::operator std::string()const + { + std::stringstream ss; + ss << to_uint64(); + ss << '.'; + auto frac = *this * real128( uint128(-1,0) ); + frac += real128(1); + + ss << std::string( frac.fixed ).substr(1); + + auto number = ss.str(); + while( number.back() == '0' ) number.pop_back(); + + return number; + } + + void to_variant( const real128& var, variant& vo ) + { + vo = std::string(var); + } + void from_variant( const variant& var, real128& vo ) + { + vo = real128(var.as_string()); + } + +} // namespace fc