Expand description
§Spirix
A high-performance two’s complement floating-point arithmetic library with customizable precision. Spirix allows you to choose both fraction and exponent sizes independently.
§Core Types
Spirix provides two primary numeric types:
Scalar<F, E>: Real numbers with fractionFand exponentECircle<F, E>: Complex numbers with fractionsFand shared exponentE
Both types use Rust’s native signed integer types (i8, i16, i32, i64, i128) for their components, allowing you to select precision and range based on your application’s needs.
§Type Aliases for Valid Configurations
For convenience, Spirix provides type aliases for all valid Rust fraction and exponent combinations.
Here are some examples:
use spirix::{ScalarF5E3, ScalarF6E4, ScalarF5E5, ScalarF6E6, ScalarF7E3}; // Format: F#E# = 2^# fraction bits, 2^# exponent bits. // F5E3 = 32-bit fraction, 8-bit exponent (similar to f32) // F6E4 = 64-bit fraction, 16-bit exponent (similar to f64) // F5E5 = 32-bit fraction, 32-bit exponent // F6E6 = 64-bit fraction, 64-bit exponent // F7E3 = 128-bit fraction, 8-bit exponent let _ = (ScalarF5E3::ONE, ScalarF6E4::ONE, ScalarF5E5::ONE, ScalarF6E6::ONE, ScalarF7E3::ONE);Similarly for complex numbers with CircleF5E3, CircleF6E4, etc.
use spirix::{Power, Scalar, ScalarConstants}; // High-precision fraction with small range. Note: this uses Rust f64 as an intermediary! let _precise_near_one = Scalar::<i128, i8>::from(9.8696044010893586188344909998761511353_f64);
// Small fraction with huge range. let _large_magnitude_low_precision = Scalar::<i8, i128>::from(6000_i16).pow(6000_i16);§Key Features and Benefits
§0. Escaped Values
When numbers grow too large or small to maintain magnitude, Spirix creates ambiguous “escaped” values that:
- Preserve orientation information, even tho magnitude is unknown
- Can still participate in absolute operations, i.e. * and /
- Track whether they escaped toward Infinity (
exploded) or toward Zero (vanished)
use spirix::{ScalarConstants, ScalarF4E7};
// Create a value so large it loses its magnitude. let huge = ScalarF4E7::MAX * 2_i8; assert!(huge.exploded()); assert!(huge.is_positive());
// Absolute operations can continue with escaped values (* / .square() etc.) let still_exploded = huge * 3_i8; assert!(still_exploded.exploded());
// Division works too! let neg_huge = huge / -5_i8; assert!(!neg_huge.is_undefined()); // Still defined! assert!(neg_huge.is_negative());§1. Infinity and Zero Identities
Spirix implements mathematical identities in accordance with Riemann sphere principles, with proper handling of Infinity and Zero:
use spirix::{ScalarConstants, ScalarF5E3};
// Division by Zero produces Infinity. let infinity = ScalarF5E3::from(42_i8) / 0_i8; assert!(infinity.is_infinite());
// Multiplicative identities with Infinity. let still_infinite = infinity * 7_i8; // ∞ × n = ∞ (for any non-Zero n) assert!(still_infinite.is_infinite());
// Infinity times Zero is undefined. let inf_times_zero = infinity * 0_i8; // ∞ × 0 = undefined assert!(inf_times_zero.is_undefined());
// Zero is the multiplicative absorber (except with Infinity). let still_zero = ScalarF5E3::ZERO * 42_i8; // 0 × n = 0 assert!(still_zero.is_zero()); let exploded = ScalarF5E3::MAX.square(); let also_zero = 0_i8 * exploded; assert!(also_zero.is_zero()); // You can use the built-in Infinity constant. let undefined_multiply = ScalarF5E3::INFINITY * still_zero; assert!(undefined_multiply.is_undefined());
// Reciprocal relationships. let zero = ScalarF5E3::ONE / infinity; // 1/∞ = 0 assert!(zero.is_zero());
// Division by Infinity. let also_zero = ScalarF5E3::from(42_i8) / infinity; // n/∞ = 0 (for any non-infinite n) assert!(also_zero.is_zero());
// Infinity divided by Infinity is undefined. let inf_div_inf = infinity / infinity; // ∞/∞ = undefined assert!(inf_div_inf.is_undefined());
// Addition and subtraction with Infinity return Infinity (Riemann sphere: ∞ absorbs). let inf_add = infinity + 5_i8; // ∞ + n = ∞ let inf_sub = infinity - infinity; // ∞ − ∞ = ∞ assert!(inf_add.is_infinite() && inf_sub.is_infinite());
// Infinity absorbs Zero under addition too. let inf_plus_zero = infinity + 0_i8; // ∞ + 0 = ∞ assert!(inf_plus_zero.is_infinite());Infinity in Spirix represents the directionless “point at Infinity” on the Riemann sphere, a singularity that conceptually unifies various approaches to Infinity and zero.
§2. Vanished Values and Their Identities
Spirix uniquely handles infinitesimal values that approach but never equal Zero:
use spirix::{ScalarConstants, ScalarF5E3};
// Create a vanished value. let tiny = ScalarF5E3::MIN_POS / 42_i8; assert!(tiny.vanished() && tiny.is_positive());
// Adding a vanished value to a normal value is like adding Zero. let normal = ScalarF5E3::from(42_i8); let sum = normal + tiny; assert!(sum == normal);
// But adding two vanished values is undefined, as the magnitude is unknown. let tiny2 = ScalarF5E3::MIN_POS / 17_i8; let undef_sum = tiny + tiny2; assert!(undef_sum.is_undefined());
// Division by a vanished value produces an exploded result. let huge = ScalarF5E3::ONE / tiny; assert!(huge.exploded());
// Multiplying vanished returns vanished. let even_smaller = tiny * tiny2; assert!(even_smaller.vanished());§3. Tracking Undefined States
Spirix provides undefined states that preserve the initial cause:
use spirix::{ScalarConstants, ScalarF5E3};
// Division by Zero produces Infinity. let infinity = ScalarF5E3::ONE / 0_i8; assert!(infinity.is_infinite());
// But indeterminate forms are undefined. let undefined = ScalarF5E3::ZERO / 0_i8; // 0/0 is undefined assert!(undefined.is_undefined());
// Operations with undefined states propagate, preserving the original cause. let still_undefined = undefined + 42_i8; assert!(still_undefined.is_undefined());
// Specific undefined cases are tracked. let undefined_inf_div_inf = infinity / infinity; // ∞/∞ is undefined assert!(undefined_inf_div_inf.is_undefined());§4. Continuous Mathematical Functions
Spirix attempts to maintain mathematical continuity across the entire number space, even beyond exponent magnitude:
use spirix::{ScalarConstants, ScalarF5E5};
// Vanished values retain phase information. let tiny_positive = ScalarF5E5::MIN_POS.square(); assert!(tiny_positive.vanished()); assert!(tiny_positive.is_positive()); let tiny_negative = tiny_positive * -2.4_f32; assert!(tiny_negative.is_negative()); // Multiplication and divison don't truncate to Zero. let supa_tiny = tiny_positive.square(); assert!(supa_tiny != 0_i8);
// Trigonometric functions work with vanished values. let sin_tiny = tiny_positive.sin(); assert!(sin_tiny.vanished() && sin_tiny.is_positive());§5. Predictable Comparisons
Comparing values is intuitive and follows mathematical principles:
use spirix::{ScalarConstants, ScalarF5E3};
let pos_normal = ScalarF5E3::from(42_i8); let neg_normal = ScalarF5E3::from(-42_i8); let pos_exploded = ScalarF5E3::MAX * 2_i8; let neg_exploded = ScalarF5E3::MIN * 2_i8; let pos_vanished = ScalarF5E3::MIN_POS / 2_i8; let neg_vanished = pos_vanished * -1_i8; let infinity = ScalarF5E3::ONE / 0_i8;
// Escaped and Zero values maintain ordering. assert!(pos_exploded > pos_normal); assert!(pos_normal > pos_vanished); assert!(pos_vanished > 0_i8); assert!(neg_exploded < neg_normal); assert!(neg_normal < neg_vanished); assert!(neg_vanished < 0_i8);
// Infinity is not comparable to anything. assert!(!(infinity > pos_normal)); assert!(!(infinity < pos_normal));
// Undefined states don't compare with anything either, including themselves. let undefined = ScalarF5E3::ZERO / 0_i8; // 0/0 assert!(!(undefined == undefined)); assert!(!(undefined < pos_normal)); assert!(!(undefined > pos_normal)); assert!(!(undefined < infinity)); assert!(!(undefined > infinity));§6. Modulus and Modulo Operations
Spirix provides two distinct remainder operations:
§0. Mathematical Modulus (%)
The standard % operator calculates the mathematical remainder after division:
use spirix::{CircleF5E3, ScalarF5E3};
// Scalar modulus — remainder after division. let a = ScalarF5E3::from(7.5_f32); let b = ScalarF5E3::from(3_i8); let remainder = a % b; assert!(remainder == 1.5_f32);
// Circle-Circle modulus — derived from complex division. let z1 = CircleF5E3::from((7_i8, 4_i8)); let z2 = CircleF5E3::from((3.8_f32, 2.2_f32)); let _complex_remainder = z1 % z2;For Circle-Circle operations, this implements:
(a + bi) % (c + di) = ((ac + bd) + (bc − ad)*i) % (c² + d²)
use spirix::{CircleF5E3, ScalarF5E3}; let z1 = CircleF5E3::from((7_i8, 4_i8));
// Circle-Scalar modulus — based on magnitude. let s = ScalarF5E3::from(2_i8); let _magnitude_remainder = z1 % s; // Or via a Rust primitive directly — primitives are always treated as Scalars // and ALL ops work with ALL primitives :) let _magnitude_remainder = z1 % 2_i8;§1. Component-wise Modulo (.modulo())
The .modulo() method performs modulo on each component separately:
use spirix::{CircleF5E3, ScalarF5E3};
// Circle-Circle component-wise modulo — acts on each part independently. let z1 = CircleF5E3::from((7_i8, 4_i8)); // 7 + 4i let z2 = CircleF5E3::from((3_i8, 2_i8)); // 3 + 2i let _component_remainder = z1.modulo(z2); // (7 % 3) + (4 % 2)i = 1 + 0i
// Circle-Scalar component-wise modulo — both components modulo Scalar. let s = ScalarF5E3::from(2_i8); let _component_scalar_mod = z1.modulo(s); // (7 % 2) + (4 % 2)i = 1 + 0iThe component-wise formula for Circle values is:
(a + b*i).modulo(c + d*i) = (a % c) + (b % d)*iBoth operations handle special cases (undefined, exploded, vanished) appropriately, preserving mathematical consistency thruout numerical space.
§7. Circles!
Spirix provides complex number support with the Circle type:
use spirix::{CircleConstants, CircleF6E4};
// Create a complex number — note the float real, integer imag mix. let z = CircleF6E4::from((1.5_f32, 2_i8));
// Access real and imaginary parts. assert!(z.r() == 1.5_f32); let imag = z.i(); assert!(imag == 2_i8);
// Calculate magnitude. let mag = z.magnitude(); assert!(mag == 2.5_f32); // Or magnitude squared. let mag_sq = z.magnitude_squared(); assert!(mag_sq == 6.25_f32);
// Complex conjugate. let _conj = z.conjugate();
// Complex multiplication. let w = CircleF6E4::from((3_i8, -4.1_f32)); let _product = z * w * 5_i8;
// Complex constants. let i = CircleF6E4::POS_I; assert!(i.square() == -1_i8);§Getting Started
§Basic Usage
use spirix::{Circle, Scalar, ScalarF5E3};
// Create Scalar values. let a = Scalar::<i32, i8>::from(7_i8); // F5E3 alias = 32-bit (2^5) fraction, 8-bit (2^3) exponent. let b = ScalarF5E3::from(1.2020569_f32);
// Convert between types — only same-sized Scalars and Circles are interoperable. let misfit: Scalar<i16, i16> = 42_i8.into(); let _c: Scalar<i32, i8> = misfit.into(); // Cross-width conversion.
// Basic arithmetic. let _sum = a + b; let product = a * b; let _quotient = a / b; let _reciprocal = b / a; // Reciprocal via division.
// Modulo operations. let _remainder = a % b; // Mathematical modulus — remainder after division.
// Transcendental functions. let _sin_a = a.sin(); let _exp_b = b.exp(); let _natural_log_product = product.ln();
// Create a complex number. let z = Circle::<i32, i8>::from((1.5_f32, 11_i8)); let _conjugate = z.conjugate();
// Complex modulo operations. let z1 = Circle::<i32, i8>::from((5_i8, 3.6_f32)); // 5 + 3.6i let z2 = Circle::<i32, i8>::from((2.4_f32, -1_i8)); // 2.4 − i let _complex_remainder = z1 % z2; // Complex mathematical modulus. let _component_remainder = z1.modulo(z2); // Component-wise modulo.§Choosing Precision
When selecting fraction and exponent sizes, consider:
- Fraction bits: Determines precision (significant digits)
- Exponent bits: Determines range (how large/small values can be)
use spirix::Scalar;
// High precision with limited range.
type HighPrecisionNearOne = Scalar<i128, i8>;
// Medium precision with large range.
type ScientificNotation = Scalar<i32, i64>;
// Extreme range with limited precision.
type RoughApproximation = Scalar<i8, i128>;| Type | Precision (decimal digits) | Range |
|---|---|---|
| F3E3 | 2.1 digits | 10^±38.5 |
| F4E4 | 4.5 digits | 10^±9860 |
| F5E5 | 9.3 digits | 10^(10^8.81) |
| F6E6 | 18.9 digits | 10^(10^18.4) |
| F7E7 | 38.2 digits | 10^(10^37.7) |
§Extra Features!
§Bit Manipulation
Spirix supports native bit-level operations that maintain alignment and mathematical consistancy:
use spirix::{ScalarConstants, ScalarF5E3};
let a = ScalarF5E3::from(42_i8);
// Bitwise operations align fractions and return expected results. let _b = a & ScalarF5E3::from(15_i8); // Aligned bitwise AND let _c = a | ScalarF5E3::from(3_i8); // Aligned bitwise OR let _d = a ^ ScalarF5E3::from(21_i8); // Aligned bitwise XOR
// Bit shifts apply checked exponent adjustments directly. let _doubled = a << 1_u8; // Multiply by 2 let _halved = a >> 3_u8; // Divide by 8 // Spirix recognises an escape and returns a vanished Scalar. let _vanished = ScalarF5E3::MIN_POS >> 1_u8;§Mathematical Truncation
Spirix provides various mathematical truncation functions:
use spirix::ScalarF5E3;
let x = ScalarF5E3::from(3.7_f32);
let _floor_x = x.floor(); // 3 let _ceil_x = x.ceil(); // 4 let _round_x = x.round(); // 4 let _frac_x = x.frac(); // 0.7§Application Examples
§Complex Number Grid
use spirix::CircleF5E3;
// Wrapping a point to an 8×8 grid cell.
fn wrap_to_grid(point: &CircleF5E3, grid_size: &CircleF5E3) -> CircleF5E3 { point.modulo(*grid_size) }
let point = CircleF5E3::from((37.54_f32, -12.3_f32)); let grid = CircleF5E3::from((8_i8, 8_i8)); let _wrapped = wrap_to_grid(&point, &grid);§Periodic Function Mapping
use spirix::{ScalarConstants, ScalarF5E3};
// Map angle to [0, 2π) range.
fn normalize_angle(angle: ScalarF5E3) -> ScalarF5E3 { angle % ScalarF5E3::TAU }
let angle = ScalarF5E3::from(8.5_f32); let normalized = normalize_angle(angle); assert!(normalized >= ScalarF5E3::ZERO); assert!(normalized < ScalarF5E3::PI * 2_i8);Re-exports§
pub use crate::core::circle::Circle;pub use crate::core::integer::Integer;pub use crate::core::scalar::Scalar;pub use crate::constants::CircleConstants;pub use crate::constants::ScalarConstants;pub use crate::operators::Clamp;pub use crate::operators::Logarithm;pub use crate::operators::Max;pub use crate::operators::Min;pub use crate::operators::Power;pub use crate::conversions::circle_scalar::IntoScalars;pub use crate::core::circle_aliases::*;pub use crate::core::scalar_aliases::*;
Modules§
- constants
- conversions
- core
- implementations
- lut
- operators
- simd
- SIMD-optimized implementations for Spirix operations
- tensor
- Tensor operations for Spirix scalar types