src/base/ScaledInteger.cpp¶
Fixed-point arithmetic utilities (Implementation file). More...
Namespaces¶
| Name |
|---|
| sgns |
Detailed Description¶
Fixed-point arithmetic utilities (Implementation file).
Version: 1.1
Date: 2025-06-10
Copyright: Copyright (c) 2025
Luiz Guilherme Rizzatto Zucchi ([email protected])
Source code¶
#include "ScaledInteger.hpp"
#include <charconv>
#include <cmath>
#include <limits>
#include <system_error>
#include <memory>
#include <boost/multiprecision/cpp_int.hpp>
namespace sgns
{
outcome::result<std::shared_ptr<ScaledInteger>> ScaledInteger::New( uint64_t raw_value, uint64_t precision )
{
auto ptr = std::shared_ptr<ScaledInteger>( new ScaledInteger( raw_value, precision ) );
return outcome::success( ptr );
}
outcome::result<std::shared_ptr<ScaledInteger>> ScaledInteger::New( double raw_value, uint64_t precision )
{
OUTCOME_TRY( auto &&from_dbl_value, FromDouble( raw_value, precision ) );
auto ptr = std::shared_ptr<ScaledInteger>( new ScaledInteger( from_dbl_value, precision ) );
return outcome::success( ptr );
}
outcome::result<std::shared_ptr<ScaledInteger>> ScaledInteger::New( const std::string &str_value,
uint64_t precision,
ParseMode mode )
{
OUTCOME_TRY( auto &&from_str_value, FromString( str_value, precision, mode ) );
auto ptr = std::shared_ptr<ScaledInteger>( new ScaledInteger( from_str_value, precision ) );
return outcome::success( ptr );
}
outcome::result<std::shared_ptr<ScaledInteger>> ScaledInteger::New( const std::string &str_value )
{
size_t dot_pos = str_value.find( '.' );
uint64_t precision_calc;
if ( dot_pos == std::string::npos )
{
precision_calc = 0;
}
else
{
precision_calc = str_value.size() - dot_pos - 1;
}
OUTCOME_TRY( auto &&raw_value, FromString( str_value, precision_calc ) );
auto ptr = std::shared_ptr<ScaledInteger>( new ScaledInteger( raw_value, precision_calc ) );
return outcome::success( ptr );
}
ScaledInteger::ScaledInteger( uint64_t value, uint64_t precision ) : value_( value ), precision_( precision ) {}
constexpr uint64_t ScaledInteger::ScaleFactor( uint64_t precision )
{
uint64_t result = 1;
for ( uint64_t i = 0; i < precision; ++i )
{
result *= 10;
}
return result;
}
outcome::result<uint64_t> ScaledInteger::FromString( const std::string &str_value,
uint64_t precision,
ParseMode mode ) // ← added ParseMode parameter
{
if ( str_value.empty() )
{
return outcome::failure( std::make_error_code( std::errc::invalid_argument ) );
}
size_t dot_pos = str_value.find( '.' );
std::string_view integer_str, fractional_str;
if ( dot_pos != std::string::npos )
{
integer_str = std::string_view( str_value ).substr( 0, dot_pos );
fractional_str = std::string_view( str_value ).substr( dot_pos + 1 );
}
else
{
integer_str = str_value;
}
uint64_t integer_part = 0;
uint64_t fractional_part = 0;
auto [ptr_int,
ec_int] = std::from_chars( integer_str.data(), integer_str.data() + integer_str.size(), integer_part );
if ( ec_int != std::errc() || ptr_int != integer_str.data() + integer_str.size() )
{
return outcome::failure( std::make_error_code( std::errc::invalid_argument ) );
}
if ( !fractional_str.empty() )
{
if ( fractional_str.length() > precision )
{
if ( mode == ParseMode::Strict )
{
return outcome::failure( std::make_error_code( std::errc::value_too_large ) );
}
fractional_str = fractional_str.substr( 0, precision );
}
auto [ptr_frac, ec_frac] = std::from_chars( fractional_str.data(),
fractional_str.data() + fractional_str.size(),
fractional_part );
if ( ec_frac != std::errc() || ptr_frac != fractional_str.data() + fractional_str.size() )
{
return outcome::failure( std::make_error_code( std::errc::invalid_argument ) );
}
}
for ( uint64_t i = fractional_str.length(); i < precision; ++i )
{
fractional_part *= 10;
}
return outcome::success( ( integer_part * ScaleFactor( precision ) ) + fractional_part );
}
std::string ScaledInteger::ToString( uint64_t value, uint64_t precision )
{
uint64_t integer_part = value / ScaleFactor( precision );
uint64_t fractional_part = value % ScaleFactor( precision );
if ( precision == 0 )
{
return std::to_string( integer_part );
}
std::string fractional_str = std::to_string( fractional_part );
if ( fractional_str.size() < precision )
{
fractional_str.insert( 0, precision - fractional_str.size(), '0' );
}
return std::to_string( integer_part ) + "." + fractional_str;
}
outcome::result<uint64_t> ScaledInteger::FromDouble( double raw_value, uint64_t precision )
{
double factor = static_cast<double>( ScaleFactor( precision ) );
double scaled = raw_value * factor;
double rounded = std::round( scaled );
if ( rounded < 0.0 || rounded > static_cast<double>( std::numeric_limits<uint64_t>::max() ) )
{
return outcome::failure( std::make_error_code( std::errc::value_too_large ) );
}
return outcome::success( static_cast<uint64_t>( rounded ) );
}
outcome::result<uint64_t> ScaledInteger::Multiply( uint64_t a, uint64_t b, uint64_t precision )
{
using namespace boost::multiprecision;
uint128_t result = static_cast<uint128_t>( a ) * static_cast<uint128_t>( b );
result = result / static_cast<uint128_t>( ScaleFactor( precision ) );
if ( result > std::numeric_limits<uint64_t>::max() )
{
return outcome::failure( std::make_error_code( std::errc::value_too_large ) );
}
return outcome::success( static_cast<uint64_t>( result ) );
}
outcome::result<uint64_t> ScaledInteger::Divide( uint64_t a, uint64_t b, uint64_t precision )
{
using namespace boost::multiprecision;
if ( b == 0 )
{
return outcome::failure( std::make_error_code( std::errc::result_out_of_range ) );
}
uint128_t result = ( static_cast<uint128_t>( a ) * static_cast<uint128_t>( ScaleFactor( precision ) ) ) /
static_cast<uint128_t>( b );
if ( result > std::numeric_limits<uint64_t>::max() )
{
return outcome::failure( std::make_error_code( std::errc::value_too_large ) );
}
return outcome::success( static_cast<uint64_t>( result ) );
}
outcome::result<uint64_t> ScaledInteger::ConvertPrecision( uint64_t value, uint64_t from, uint64_t to )
{
using namespace boost::multiprecision;
if ( from == to )
{
return outcome::success( value );
}
if ( to > from )
{
uint64_t delta = to - from;
uint128_t result = static_cast<uint128_t>( value ) * static_cast<uint128_t>( ScaleFactor( delta ) );
if ( result > std::numeric_limits<uint64_t>::max() )
{
return outcome::failure( std::make_error_code( std::errc::value_too_large ) );
}
return outcome::success( static_cast<uint64_t>( result ) );
}
uint64_t delta = from - to;
uint64_t converted = value / ScaleFactor( delta );
return outcome::success( converted );
}
uint64_t ScaledInteger::Value() const noexcept
{
return value_;
}
uint64_t ScaledInteger::Precision() const noexcept
{
return precision_;
}
std::string ScaledInteger::ToString( bool fixedDecimals ) const
{
std::string s = ToString( value_, precision_ );
if ( !fixedDecimals )
{
auto dotPos = s.find( '.' );
if ( dotPos != std::string::npos )
{
auto lastNonZero = s.find_last_not_of( '0' );
if ( lastNonZero != std::string::npos )
{
s.erase( lastNonZero + 1 );
}
if ( !s.empty() && s.back() == '.' )
{
s.pop_back();
}
}
}
return s;
}
outcome::result<ScaledInteger> ScaledInteger::Add( const ScaledInteger &other ) const
{
if ( precision_ != other.precision_ )
{
return outcome::failure( std::make_error_code( std::errc::invalid_argument ) );
}
using namespace boost::multiprecision;
uint128_t sum = static_cast<uint128_t>( value_ ) + static_cast<uint128_t>( other.value_ );
if ( sum > std::numeric_limits<uint64_t>::max() )
{
return outcome::failure( std::make_error_code( std::errc::value_too_large ) );
}
return outcome::success( ScaledInteger( static_cast<uint64_t>( sum ), precision_ ) );
}
outcome::result<ScaledInteger> ScaledInteger::Subtract( const ScaledInteger &other ) const
{
if ( precision_ != other.precision_ )
{
return outcome::failure( std::make_error_code( std::errc::invalid_argument ) );
}
if ( other.value_ > value_ )
{
return outcome::failure( std::make_error_code( std::errc::result_out_of_range ) );
}
return outcome::success( ScaledInteger( value_ - other.value_, precision_ ) );
}
outcome::result<ScaledInteger> ScaledInteger::Multiply( const ScaledInteger &other ) const
{
if ( precision_ != other.precision_ )
{
return outcome::failure( std::make_error_code( std::errc::invalid_argument ) );
}
OUTCOME_TRY( auto &&multiply_res, ScaledInteger::Multiply( value_, other.value_, precision_ ) );
return outcome::success( ScaledInteger( multiply_res, precision_ ) );
}
outcome::result<ScaledInteger> ScaledInteger::Divide( const ScaledInteger &other ) const
{
if ( precision_ != other.precision_ )
{
return outcome::failure( std::make_error_code( std::errc::invalid_argument ) );
}
OUTCOME_TRY( auto &÷_res, ScaledInteger::Divide( value_, other.value_, precision_ ) );
return outcome::success( ScaledInteger( divide_res, precision_ ) );
}
outcome::result<ScaledInteger> ScaledInteger::ConvertPrecision( uint64_t to ) const
{
OUTCOME_TRY( auto &&convert_res, ScaledInteger::ConvertPrecision( value_, precision_, to ) );
return outcome::success( ScaledInteger( convert_res, to ) );
}
} // namespace sgns
Updated on 2026-03-04 at 13:10:44 -0800