| Crates.io | spirix |
| lib.rs | spirix |
| version | 0.0.5 |
| created_at | 2025-02-13 21:40:36.81345+00 |
| updated_at | 2025-09-25 01:59:28.376403+00 |
| description | Two's complement floating-point arithmetic library |
| homepage | https://github.com/nickspiker/spirix |
| repository | https://github.com/nickspiker/spirix |
| max_upload_size | |
| id | 1554926 |
| size | 6,151,322 |
This is early beta software under active development. While the core arithmetic operations and many mathematical functions have been tested fairly extensively, this library is not ready for production use. Please do not use this library in critical systems or applications where incorrect calculations could cause harm.
Current status:
Use at your own risk and always validate results independently for important maths.
Spirix is a high-performance numeric library that implements a fundamentally new approach to floating-point arithmetic by utilizing two's complement representation thruout the entire calculation pipeline. Unlike traditional floating-point implementations that use separate sign bit and magnitude representation, Spirix employs a continuous numeric representation with left-aligned normalized fractions and unbiased exponents.
This approach provides several key advantages:
Spirix's core innovation is using two's complement representation for floating-point numbers, which enables:
Where traditional floating-point has a sign bit followed by exponent and mantissa:
IEEE-754: [sign bit][biased exponent][mantissa]
Spirix uses left-aligned two's complement fractions with unbiased two's compliment exponents:
Spirix: [normalized fraction][unbiased exponent]
This representation allows for:
Spirix introduces a novel normalization level system that encodes the normal/ambiguous state thru patterns in the most significant bits:
| LSB/N-level | Bit Pattern | Description | Symbol |
|---|---|---|---|
| N-0 | □□□□□□□□ | Zero | [0] |
| N-0 | ■■■■■■■■ | General undefined | [℘] |
| N-1 | □■xxxxxx | Positive normal or exploded | [+#], [+↑] |
| N-1 | ■□xxxxxx | Negative normal or exploded | [-#], [-↑] |
| N-2 | □□■xxxxx | Positive vanished | [+↓] |
| N-2 | ■■□xxxxx | Negative vanished | [-↓] |
| N-3+ | □□□xxxxx | Various undefined states | [℘ 'state'] |
| N-3+ | ■■■xxxxx | Various undefined states | [℘ 'state'] |
Note that Zero and general undefined are uniform patterns, both of which have an ambiguous exponent. Each N level is distinguished by checking LSB (leading same bits). Ambiguous values are marked by having an exponent set to the lowest possible value in the exponent, referred to as AMBIGUOUS_EXPONENT. Ambiguous values include Zero, Infinity, Undefined, Vanished and Exploded. Exploded have LSB of one N-1 (same as normal states but with AMBIGUOUS_EXPONENT). Vanished have LSB of two with AMBIGUOUS EXPONENT. Undefined contain three or more LSB with AMBIGUOUS_EXPONENT. Zero contains a fraction of all zeros, allowing fast determination of a value's normality or ambiguous state without unnecessary testing or branching.
When a value exceeds the normal exponent range, Spirix doesn't simply truncate to infinity or zero. Instead, it creates an "escaped" phase that preserves sign or orientation information:
These escaped values can continue to participate in absolute mathematical operations, like multiplication and division, allowing calculations to proceed even with results beyond the exponent range. Escaped Scalars maintain their sign, escaped Circles maintain their angle/complex sign thru absolute operations.
Spirix provides two primary numeric types:
F and exponent EF and shared exponent EBoth types are parameterized to allow independent selection of fraction and exponent sizes based on application needs.
Spirix provides a flexible configuration system allowing independent selection of fraction and exponent sizes. This enables applications to precisely tune numerical behavior based on their specific needs.
| 2.2 digits | 4.6 digits | 9.4 digits | 19 digits | 38.3 digits | |
|---|---|---|---|---|---|
| 10^±2.1 | F3E3 ⟨i8, i8⟩ |
F4E3 ⟨i16, i8⟩ |
F5E3 ⟨i32, i8⟩ |
F6E3 ⟨i64, i8⟩ |
F7E3 ⟨i128, i8⟩ |
| 10^±4.5 | F3E4 ⟨i8, i16⟩ |
F4E4 ⟨i16, i16⟩ |
F5E4 ⟨i32, i16⟩ |
F6E4 ⟨i64, i16⟩ |
F7E4 ⟨i128, i16⟩ |
| 10^±9.3 | F3E5 ⟨i8, i32⟩ |
F4E5 ⟨i16, i32⟩ |
F5E5 ⟨i32, i32⟩ |
F6E5 ⟨i64, i32⟩ |
F7E5 ⟨i128, i32⟩ |
| 10^±18.9 | F3E6 ⟨i8, i64⟩ |
F4E6 ⟨i16, i64⟩ |
F5E6 ⟨i32, i64⟩ |
F6E6 ⟨i64, i64⟩ |
F7E6 ⟨i128, i64⟩ |
| 10^±38.2 | F3E7 ⟨i8, i128⟩ |
F4E7 ⟨i16, i128⟩ |
F5E7 ⟨i32, i128⟩ |
F6E7 ⟨i64, i128⟩ |
F7E7 ⟨i128, i128⟩ |
Scalar values are calculated as:
value = fraction × 2^exponent
Circle values have real and imaginary phases sharing a common exponent:
value = (real + imaginary × i) × 2^exponent
Normal values have definite magnitudes and participate fully in all arithmetic operations:
□■xxxxxx... - Positive normal numbers
■□xxxxxx... - Negative normal numbers
Normal numbers have:
Zero is represented with a unique bit pattern in the fraction:
□□□□□□□□... - Zero
All zeros. Zero is ambiguous, thus has exponent equal to AMBIGIOUS_EXPONENT
Infinity is represented with a unique bit pattern in the fraction:
■■■■■■■■... - Infinity
All ones. Infinity is ambiguous, thus has exponent equal to AMBIGIOUS_EXPONENT
When values exceed the representable range, they become "escaped" values:
Exploded values [↑]: Numbers too large to represent with current exponent range
□■xxxxxx... N-1 fraction with AMBIGUOUS_EXPONENT - Positive exploded [+↑]
■□xxxxxx... N-1 fraction with AMBIGUOUS_EXPONENT - Negative exploded [-↑]
Vanished values [↓]: Numbers too small to represent with current exponent range
□□■xxxxx... N-2 fraction with AMBIGUOUS_EXPONENT - Positive vanished [+↓]
■■□xxxxx... N-2 fraction with AMBIGUOUS_EXPONENT - Negative vanished [-↓]
Escaped values maintain mathematical continuity and can participate in absolute operations while preserving phase information.
When operations produce mathematically undefined results, Spirix returns distinct undefined states that preserve the cause:
■■■■■■■■... with AMBIGUOUS_EXPONENT - General undefined
□□□■xxxx... with AMBIGUOUS_EXPONENT - Specific undefined states
■■■□xxxx... with AMBIGUOUS_EXPONENT - Specific undefined states
Undefined states propagate thru operations, preserving their cause.
Note that Rust primitives like f32, i8 are treated as Scalars. For convenience, Circles can be converted to/from Num::Complex::
Spirix Rust native operations supported:
// Basic operations
let sum = a + b; // Addition
let diff = a - b; // Subtraction
let product = a * b; // Multiplication
let quotient = a / b; // Division
let remainder = a % b; // Mathematical remainder
let component_mod = a.modulo(b); // Component-wise remainder (for Circle)
let negated = -a; // Negation
let reciprocal = a.reciprocal(); // 1/a
// Safe aligned bitwise operations let bit_and = a & b; // Bitwise AND let bit_or = a | b; // Bitwise OR let bit_xor = a ^ b; // Bitwise XOR let bit_not = !a; // Bitwise NOT let left_shift = a << 2; // Left shift by integer (multiply by 2^2) let right_shift = a >> 1; // Right shift by integer (divide by 2^1)
## Complex Number Operations
```rust
// Complex-specific operations
let conj = z.conjugate(); // Complex conjugate
let mag = z.magnitude(); // Distance from origin
let mag_squared = z.magnitude_squared(); // Squared magnitude (faster)
let unit = z.sign(); // Unit vector in same direction
let components = (z.r(), z.i()); // Extract real and imaginary parts as Scalars
// Power operations
let squared = x.square(); // x*x
let root = x.sqrt(); // Square root
let result = x.pow(y); // Power
// Logarithmic operations
let natural_log = x.ln(); // Natural logarithm (base e)
let binary_log = x.lb(); // Binary logarithm (base 2)
let custom_log = x.log(base); // Logarithm with custom base
let exponential = x.exp(); // e^x
let power_of_two = x.powb(); // 2^x
// Standard trigonometric functions
let sine = x.sin(); // Sine
let cosine = x.cos(); // Cosine
let tangent = x.tan(); // Tangent
// Inverse trigonometric functions
let arcsine = x.asin(); // Arc sine
let arccosine = x.acos(); // Arc cosine
let arctangent = x.atan(); // Arc tangent
let atan2 = y.atan2(x); // Two-argument arctangent
// Hyperbolic functions
let hyperbolic_sine = x.sinh(); // Hyperbolic sine
let hyperbolic_cosine = x.cosh(); // Hyperbolic cosine
let hyperbolic_tangent = x.tanh(); // Hyperbolic tangent
let inverse_hyperbolic_sine = x.asinh(); // Inverse hyperbolic sine
let inverse_hyperbolic_cosine = x.acosh(); // Inverse hyperbolic cosine
let inverse_hyperbolic_tangent = x.atanh(); // Inverse hyperbolic tangent
// Integer functions
let floor_value = x.floor(); // Greatest integer ≤ x
let ceiling = x.ceil(); // Smallest integer ≥ x
let rounded = x.round(); // Nearest integer, ties to even
let fractional = x.frac(); // Part after decimal
// Number properties
let is_integer = x.is_integer(); // Checks if value is an integer
let is_contiguous = x.is_contiguous(); // Checks if in contiguous range
let is_prime = x.is_prime(); // Tests for primality
// State testing
let normal = x.is_normal(); // Standard numeric value
let zero = x.is_zero(); // Actual Zero
let negligible = x.is_negligible(); // Zero or vanished
let tiny = x.vanished(); // Escaped small value
let huge = x.exploded(); // Escaped large value
let undefined = x.is_undefined(); // Undefined state
let finite = x.is_finite(); // Normal or Zero
// Sign testing
let positive = x.is_positive(); // Greater than Zero
let negative = x.is_negative(); // Less than Zero
// Random values
let uniform = ScalarF5E3::random(); // Uniform -1 to 1 distribution
let gaussian = ScalarF6E4::random_gauss(); // Normal distribution
// For complex numbers
let complex_uniform = CircleF7E5::random(); // Uniform inside unit circle
let complex_gaussian = CircleF4E4::random_gauss(); // Normal distribution
// Min, max, clamp
let minimum = a.min(b); // Smaller value
let maximum = a.max(b); // Larger value
let constrained = x.clamp(min, max); // Value within bounds
Spirix tracks the cause of undefined operations with specific bit patterns:
| Undefined State | Description |
|---|---|
℘ ⬆+⬆ |
Transfinite value addition with transfinite value |
℘ ⬆-⬆ |
Transfinite value subtraction with transfinite value |
℘ ↓+↓ |
Vanished value addition with vanished value |
℘ ↓-↓ |
Vanished value subtraction with vanished value |
℘ ⬆+ |
Transfinite value addition with finite value |
℘ ⬆- |
Transfinite value subtraction with finite value |
℘ ⨅∞ |
Fractional part of Infinity |
℘ ±∅ |
Sign/direction of Zero or Infinity is indeterminate |
℘ ⊥⊙ |
Indeterminate Scalar → Circle conversion |
℘ ∩ |
Clamp with non-ordered ambiguous values |
℘ ⌈ |
Maximum of non-ordered ambiguous values |
℘ ⌊ |
Minimum of non-ordered ambiguous values |
℘ +⬆ |
Finite value addition with transfinite value |
℘ -⬆ |
Finite value subtraction with transfinite value |
℘ ⬆/⬆ |
Transfinite value division by transfinite value |
℘ ⬇/⬇ |
Negligible value division by negligible value |
℘ ⬆% |
Transfinite value modulus operation |
℘ ⬆‰ |
Transfinite value modulo operation |
℘ %↓ |
Finite value modulus with vanished value |
℘ ‰↓ |
Finite value modulo with vanished value |
℘ %↑ |
Modulus with exploded denominator and mismatched signs |
℘ ‰↑ |
Modulo with exploded denominator and mismatched signs |
℘ & |
Logical AND with escaped value |
℘ | |
Logical OR with escaped value |
℘ ⊻ |
Logical XOR with escaped value |
℘ ⬇×⬆ |
Negligible value multiplication with transfinite value |
℘ ⬆×⬇ |
Transfinite value multiplication with negligible value |
℘ ⬆^ |
Transfinite value raised to power |
℘ ⬇^ |
Vanished value raised to power |
℘ ^⬆ |
Value raised to transfinite power |
℘ ^⬇ |
Value raised to vanished power |
℘ -^ |
Negative value raised to irrational power |
℘ @1 |
Logarithm base One |
℘ √- |
Square root of negative value |
℘ √↑ |
Square root of transfinite value |
℘ √↓ |
Square root of vanished value |
℘ ⬆@ |
Logarithm of transfinite value |
℘ ⬇@ |
Logarithm of negligible value |
℘ @⬆ |
Logarithm with transfinite base |
℘ @⬇ |
Logarithm with negligible base |
℘ -@ |
Logarithm of negative value |
℘ @- |
Logarithm with negative base |
℘ s |
Sine of value with imprecise period position |
℘ c |
Cosine of value with imprecise period position |
℘ S |
Arcsine of value outside domain [-1,1] |
℘ C |
Arccosine of value outside domain [-1,1] |
℘ t |
Tangent of value with imprecise period position |
℘ |
General undefined or unimplemented and extensions |
These undefined states propagate thru operations, preserving the first cause of the undefined condition.
use spirix::{Scalar, ScalarF5E3};
// Create a Scalar with explicitly specified type parameters
let a = Scalar::<i32, i8>::from(42);
// Create a Scalar using a type alias
let b = ScalarF5E3::from(3.14159);
// Convert from Rust literals
let c: ScalarF5E3 = 2.71828.into();
// Create from constants
let pi = ScalarF5E3::PI;
let e = ScalarF5E3::E;
use spirix::{Scalar, ScalarF6E4};
let a = ScalarF6E4::from(7);
let b = ScalarF6E4::from(3);
// Basic operations
let sum = a + b; // 10
let difference = a - b; // 4
let product = a * b; // 21
let quotient = a / b; // 2.33333...
// Transcendental functions
let sin_a = a.sin();
let exp_b = b.exp(); // e^3
let log_ab = (a * b).ln(); // ln(21)
use spirix::{Circle, CircleF5E3};
// Create a complex number (real, imaginary)
let z = CircleF5E3::from((3.0, 4.0)); // 3 + 4i
// Access components
let real = z.r(); // 3.0
let imag = z.i(); // 4.0
// Complex arithmetic
let w = CircleF5E3::from((1.0, -2.0)); // 1 - 2i
let sum = z + w; // 4 + 2i
let product = z * w; // 11 - 2i
// Circle constants
let i = CircleF5E3::POS_I; // 0 + 1i
let two_pi_i = CircleF5E3::TAU * i; // 0 + 2πi
// Complex-specific operations
let conj = z.conjugate(); // 3 - 4i
let mag = z.magnitude(); // 5
use spirix::{Scalar, ScalarF5E3};
// Create escaped values
let huge = ScalarF5E3::MAX * 2;
assert!(huge.exploded());
assert!(huge.is_positive());
let tiny = ScalarF5E3::MIN_POS / 42;
assert!(tiny.vanished());
assert!(tiny.is_positive());
// Operations with escaped values
let still_exploded = huge * 3; // Still exploded
let neg_huge = huge * -1; // Negative exploded
let zero_like = huge * 0; // Actual zero
// Undefined states
let div_by_zero = ScalarF5E3::ONE / 0;
assert!(div_by_zero.is_undefined());
// Checking value state
if value.is_normal() {
// Process normal value
} else if value.vanished() {
// Handle vanished value
} else if value.exploded() {
// Handle exploded value
} else if value.is_undefined() {
// Handle undefined state
} else if value.is_zero() {
// Handle zero value
}
Spirix's design emphasizes efficiency in several ways:
Reduced branching: The two's complement representation eliminates most sign-specific code paths, resulting in fewer branches and more predictable execution.
Efficient state detection: The normalization level system allows quick determination of value state without extensive testing.
Branchless algorithms: Many operations can be implemented with few or no conditional branches, making them ideal for SIMD processing.
Parametric sizing: The ability to choose fraction and exponent sizes allows applications to optimize for their specific precision and range needs without unnecessary overhead.
For maximum performance:
Spirix differs from traditional floating-point implementations in several key ways:
| Feature | Traditional FP | Spirix |
|---|---|---|
| Sign representation | Separate sign bit | Two's complement thruout |
| Number line | Discontinuous at zero | Continuous thru entire range |
| Special values | Positive/negative infinity, NaN | Exploded, vanished, and specific undefined states |
| Denormal numbers | Gradual precision loss | Vanished values with sign preservation |
| Error information | Single NaN value | Multiple specific undefined states |
| Bit manipulation | Requires int conversion | Directly supported with alignment |
| Complex support | Separate real/imaginary | Unified Circle type with shared exponent |
| Precision/range | Fixed configurations | Independently configurable |
Spirix maintains fundamental mathematical identities that IEEE-754 violates:
Both IEEE-754 and Spirix preserve this identity:
let normal_ieee = 5.;
assert!(normal_ieee - normal_ieee == 0.);
let normal_scalar : ScalarF6E5 = 5.into();
assert!(normal_scalar - normal_scalar == 0);
IEEE-754 violates this fundamental property, while Spirix preserves it:
let tiny_ieee = f64::MIN_POSITIVE * f64::MIN_POSITIVE; // Underflows to 0
assert!(tiny_ieee.is_zero());
let tiny_scalar = ScalarF7E5::MIN_POS.square(); // Returns a vanished scalar, not Zero
assert!(!tiny_scalar.is_zero());
These differences make Spirix particularly well-suited for: