initial check-in
This commit is contained in:
@@ -0,0 +1 @@
|
||||
example
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user