initial check-in

This commit is contained in:
Matt Hess
2026-01-25 16:22:30 -07:00
parent 015a8dde35
commit b4e764afa0
4 changed files with 298 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
example
+42
View File
@@ -0,0 +1,42 @@
# Salvium: Fixed-Point Decimal Type for Cryptocurrency
## Design Principles
**No silent failures**: Arithmetic operations throw on overflow/underflow instead of clamping. This surfaces bugs immediately rather than corrupting balances silently.
**Integer-based storage**: Stores values as uint64_t atomic units (dusties or 1e-8 Salvium), eliminating floating-point rounding errors entirely. 8 decimal places matches Salvium's precision requirements.
**Type-safe**: The type system itself prevents negative values (unsigned backing type) rather than relying on runtime checks. You cannot construct an invalid state.
**Explicit bounds checking**: Operations that could overflow check before executing, making resource limits visible in the code.
## Why This Works for Crypto
1. **Precision**: Fixed-point arithmetic with integer backing guarantees exact results. No floating-point rounding corruption.
2. **Atomicity**: Works directly with atomic units (dusties), which is how blockchains actually store values.
3. **Auditability**: Failed operations throw; you know immediately when something went wrong. No silently-clamped balance errors.
4. **No negative values by design**: Uses unsigned integers, not runtime validation that can fail.
## Usage
```cpp
// From atomic units
Salvium balance(500000000ULL); // 5 Salvium
// From decimal string (no floating-point involved)
Salvium amount = Salvium::from_string("1.5");
// Arithmetic throws on overflow/underflow
balance += amount;
balance -= amount; // throws if amount > balance
// Get values back
uint64_t atomic = balance.to_atomic();
std::string display = balance.to_salvium_string();
```
This is what crypto needs.
+42
View File
@@ -0,0 +1,42 @@
#include "salvium.h"
#include <iostream>
int main() {
try {
// Create amounts from atomic units
Salvium balance(500000000ULL); // 5 Salvium
Salvium payment(150000000ULL); // 1.5 Salvium
std::cout << "Balance: " << balance.to_salvium_string() << " Salvium\n";
std::cout << "Payment: " << payment.to_salvium_string() << " Salvium\n";
// Add payment
balance += payment;
std::cout << "After deposit: " << balance.to_salvium_string() << " Salvium\n";
// Subtract payment
Salvium withdrawal = Salvium::from_string("2.3");
balance -= withdrawal;
std::cout << "After withdrawal: " << balance.to_salvium_string() << " Salvium\n";
// This will throw
try {
balance -= Salvium::from_string("100");
} catch (const std::underflow_error& e) {
std::cout << "Error caught: " << e.what() << "\n";
}
// Demonstrate multiplication (pool payout scaling)
Salvium block_reward = Salvium::from_string("17.5");
Salvium miner_share = block_reward * 50 / 100; // 50% share
std::cout << "Block reward: " << block_reward.to_salvium_string() << " Salvium\n";
std::cout << "Miner share: " << miner_share.to_salvium_string() << " Salvium\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
return 1;
}
}
+213
View File
@@ -0,0 +1,213 @@
#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