214 lines
6.3 KiB
C++
214 lines
6.3 KiB
C++
#ifndef SALVIUM_H
|
|
#define SALVIUM_H
|
|
|
|
#include <cstdint>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
|
|
// Fixed-point decimal type for Salvium atomic units
|
|
// Stores values as uint64_t with 8 decimal places (dusty precision)
|
|
// Arithmetic operations fail loudly on overflow/underflow
|
|
|
|
class Salvium {
|
|
private:
|
|
static constexpr uint64_t ATOMIC_UNIT = 100000000ULL; // 10^8
|
|
static constexpr int DECIMAL_PLACES = 8;
|
|
|
|
uint64_t atomic_units; // value in dusties
|
|
|
|
// Helper to detect overflow in multiplication
|
|
static bool will_overflow_multiply(uint64_t a, uint64_t b) {
|
|
if (a == 0 || b == 0) return false;
|
|
return a > UINT64_MAX / b;
|
|
}
|
|
|
|
// Helper to detect overflow in addition
|
|
static bool will_overflow_add(uint64_t a, uint64_t b) {
|
|
return a > UINT64_MAX - b;
|
|
}
|
|
|
|
public:
|
|
// Construct from atomic units (dusties)
|
|
explicit Salvium(uint64_t atomic = 0) : atomic_units(atomic) {}
|
|
|
|
// Construct from decimal string (e.g., "1.5", "100", "0.00000001")
|
|
// Parses directly to atomic units without floating-point
|
|
static Salvium from_string(const std::string& s) {
|
|
if (s.empty()) {
|
|
throw std::invalid_argument("Empty string");
|
|
}
|
|
|
|
size_t decimal_pos = s.find('.');
|
|
std::string whole_str = (decimal_pos == std::string::npos) ? s : s.substr(0, decimal_pos);
|
|
std::string frac_str = (decimal_pos == std::string::npos) ? "" : s.substr(decimal_pos + 1);
|
|
|
|
// Validate characters
|
|
for (char c : whole_str) {
|
|
if (c < '0' || c > '9') {
|
|
throw std::invalid_argument("Invalid character in whole part");
|
|
}
|
|
}
|
|
for (char c : frac_str) {
|
|
if (c < '0' || c > '9') {
|
|
throw std::invalid_argument("Invalid character in fractional part");
|
|
}
|
|
}
|
|
|
|
// Check fractional precision
|
|
if (frac_str.length() > DECIMAL_PLACES) {
|
|
throw std::invalid_argument("Too many decimal places (max 8)");
|
|
}
|
|
|
|
// Pad fractional part to 8 digits
|
|
while (frac_str.length() < DECIMAL_PLACES) {
|
|
frac_str += '0';
|
|
}
|
|
|
|
// Parse parts
|
|
uint64_t whole = 0;
|
|
if (!whole_str.empty()) {
|
|
for (char c : whole_str) {
|
|
uint64_t digit = c - '0';
|
|
if (will_overflow_multiply(whole, 10)) {
|
|
throw std::overflow_error("Value exceeds maximum");
|
|
}
|
|
whole *= 10;
|
|
if (will_overflow_add(whole, digit)) {
|
|
throw std::overflow_error("Value exceeds maximum");
|
|
}
|
|
whole += digit;
|
|
}
|
|
}
|
|
|
|
uint64_t frac = 0;
|
|
for (char c : frac_str) {
|
|
frac = frac * 10 + (c - '0');
|
|
}
|
|
|
|
// Combine: whole * ATOMIC_UNIT + frac
|
|
if (will_overflow_multiply(whole, ATOMIC_UNIT)) {
|
|
throw std::overflow_error("Value exceeds maximum");
|
|
}
|
|
uint64_t atomic = whole * ATOMIC_UNIT;
|
|
if (will_overflow_add(atomic, frac)) {
|
|
throw std::overflow_error("Value exceeds maximum");
|
|
}
|
|
|
|
return Salvium(atomic + frac);
|
|
}
|
|
|
|
// Get value in atomic units (dusties)
|
|
uint64_t to_atomic() const {
|
|
return atomic_units;
|
|
}
|
|
|
|
// Get value as display string (Salvium)
|
|
std::string to_salvium_string() const {
|
|
std::ostringstream oss;
|
|
oss << std::fixed << std::setprecision(DECIMAL_PLACES);
|
|
|
|
uint64_t whole = atomic_units / ATOMIC_UNIT;
|
|
uint64_t fractional = atomic_units % ATOMIC_UNIT;
|
|
|
|
oss << whole << "." << std::setfill('0') << std::setw(DECIMAL_PLACES) << fractional;
|
|
|
|
std::string result = oss.str();
|
|
|
|
// Strip trailing zeros after decimal point
|
|
size_t decimal_pos = result.find('.');
|
|
if (decimal_pos != std::string::npos) {
|
|
result.erase(result.find_last_not_of('0') + 1);
|
|
if (result.back() == '.') {
|
|
result.pop_back();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Arithmetic operators
|
|
Salvium operator+(const Salvium& other) const {
|
|
if (will_overflow_add(atomic_units, other.atomic_units)) {
|
|
throw std::overflow_error("Addition overflow");
|
|
}
|
|
return Salvium(atomic_units + other.atomic_units);
|
|
}
|
|
|
|
Salvium operator-(const Salvium& other) const {
|
|
if (atomic_units < other.atomic_units) {
|
|
throw std::underflow_error("Subtraction underflow");
|
|
}
|
|
return Salvium(atomic_units - other.atomic_units);
|
|
}
|
|
|
|
Salvium operator*(uint64_t scalar) const {
|
|
if (will_overflow_multiply(atomic_units, scalar)) {
|
|
throw std::overflow_error("Multiplication overflow");
|
|
}
|
|
return Salvium(atomic_units * scalar);
|
|
}
|
|
|
|
Salvium operator/(uint64_t scalar) const {
|
|
if (scalar == 0) {
|
|
throw std::invalid_argument("Division by zero");
|
|
}
|
|
return Salvium(atomic_units / scalar);
|
|
}
|
|
|
|
// In-place operators
|
|
Salvium& operator+=(const Salvium& other) {
|
|
*this = *this + other;
|
|
return *this;
|
|
}
|
|
|
|
Salvium& operator-=(const Salvium& other) {
|
|
*this = *this - other;
|
|
return *this;
|
|
}
|
|
|
|
Salvium& operator*=(uint64_t scalar) {
|
|
*this = *this * scalar;
|
|
return *this;
|
|
}
|
|
|
|
Salvium& operator/=(uint64_t scalar) {
|
|
*this = *this / scalar;
|
|
return *this;
|
|
}
|
|
|
|
// Comparison operators
|
|
bool operator==(const Salvium& other) const {
|
|
return atomic_units == other.atomic_units;
|
|
}
|
|
|
|
bool operator!=(const Salvium& other) const {
|
|
return atomic_units != other.atomic_units;
|
|
}
|
|
|
|
bool operator<(const Salvium& other) const {
|
|
return atomic_units < other.atomic_units;
|
|
}
|
|
|
|
bool operator<=(const Salvium& other) const {
|
|
return atomic_units <= other.atomic_units;
|
|
}
|
|
|
|
bool operator>(const Salvium& other) const {
|
|
return atomic_units > other.atomic_units;
|
|
}
|
|
|
|
bool operator>=(const Salvium& other) const {
|
|
return atomic_units >= other.atomic_units;
|
|
}
|
|
|
|
// Zero check
|
|
bool is_zero() const {
|
|
return atomic_units == 0;
|
|
}
|
|
};
|
|
|
|
#endif // SALVIUM_H
|
|
|