modulus implementation
.NET Test / .NET tests (push) Successful in 2m8s

This commit is contained in:
2026-09-24 02:25:35 +04:00
parent cb06df8865
commit 4a7ce1b8ed
7 changed files with 646 additions and 8 deletions
+32 -6
View File
@@ -22,20 +22,37 @@ already-normalized results. Mathematical constants use precomputed high/low pair
checked against independently computed high-precision values; accessing them does
not perform double-double arithmetic or allocate on the heap.
- Arithmetic: static `Add`, `Subtract`, `Multiply`, and `Divide` methods accept
- Arithmetic: static `Add`, `Subtract`, `Multiply`, `Divide`, and `Modulus` methods accept
`DoubleDouble` pairs and both operand orders with a `double`. `Plus` and `Negate`
provide unary operations. The arithmetic operators (`+`, `-`, `*`, `/`, unary
provide unary operations. The arithmetic operators (`+`, `-`, `*`, `/`, `%`, unary
`+`/`-`, and `++`/`--`) are thin wrappers around these methods.
Addition retains residuals under cancellation; multiplication uses
fused multiply-add; division uses residual corrections. Mixed `double` operators
fused multiply-add; division uses residual corrections. Mixed `double` overloads
use specialized scalar paths rather than promoting the scalar to `DoubleDouble`.
Division retains the complete first remainder and applies two quotient corrections,
normalizing before the final correction. Mixed addition and subtraction also retain
intermediate sum residuals through final normalization. Mixed addition, subtraction,
and division produce the same component bits as their `DoubleDouble` operations
with the scalar represented as a zero-low pair.
- Remainder: `Modulus` and `%` use C# truncation-toward-zero semantics, not
Euclidean modulo or IEEE nearest-integer remainder. Both components determine
the exact integer quotient; the remainder is then rounded to a normalized pair.
Its sign, including zero, follows the dividend. A zero divisor, infinite dividend,
or NaN operand produces canonical NaN; a finite dividend modulo infinity is
unchanged. Zero-low operands use binary64 `%`; smaller dividends and equal-high
operands also have non-allocating shortcuts. The general finite path uses FMA
residuals and stack-based error-free sums to verify the exact integer quotient
before rounding the remainder. Its initial quotient estimate is limited to
`2^52` and the dividend's high exponent to 1020, leaving safe transform headroom.
Outside those bounds, or if quotient correction cannot be certified, bounded
`BigInteger` arithmetic is the fallback. Warmed allocation tests pin zero managed
allocations for representative two-component cases in all three overloads;
no timing improvement is claimed. Mixed overloads retain the component bits of the
equivalent DD/DD operation. Exact-dyadic tests and independent rational checks
cover sparse lows, quotient boundaries, subnormals and extreme exponents; this
does not promise universally correctly rounded component pairs.
- Exponent boundaries: bounded `BigInteger` calculations avoid intermediate
overflow and underflow on the exceptional finite path. Ordinary arithmetic uses
overflow and underflow on the exceptional finite path. Ordinary `+`, `-`, `*`, `/` use
floating-point transforms without allocations, although sparse division correction
products can also reach a boundary path. The stored value remains two
doubles; this is not an arbitrary-precision API.
@@ -389,13 +406,22 @@ sampled error bounds does not guarantee correct range decisions for every input.
to recover the components. This is not a shortest-round-trip format and does not
change the standard formats' 999-digit precision limit or the parser's length limit.
`DoubleDouble` implements `ISignedNumber<DoubleDouble>`, including the inherited
`DoubleDouble` implements `INumber<DoubleDouble>` and `ISignedNumber<DoubleDouble>`, including the inherited
`INumberBase` contracts: binary radix, classification, absolute value, magnitude
selection, increment/decrement, and generic numeric conversions. Integer/parity
tests and magnitude comparisons retain both components. Magnitude ties prefer
positive values for maximum and negative values for minimum, including signed zero;
the `Number` variants prefer a number over NaN.
`INumber` provides comparison and remainder dispatch as well as constrained generic
access to `Clamp`, `CopySign`, `Max`, `Min`, `MaxNumber`, and `MinNumber` through
the interface defaults. The .NET 10 native variants also use their defaults.
These defaults do not create directly callable static methods on `DoubleDouble`.
`DoubleDouble.Sign` is explicitly implemented: it returns -1, 0, or 1 and throws
`ArithmeticException` for NaN consistently on both target frameworks (unlike the
.NET 8 interface default). Comparisons and sign copying preserve low components
and signed-zero behavior; NaN results retain the library's canonical representation.
`CreateChecked`, `CreateSaturating`, and `CreateTruncating` support built-in numeric
types and `BigInteger`. Floating overflow produces signed infinity in all modes.
Finite integer output truncates the exact sum, then throws on overflow, clamps,
@@ -459,7 +485,7 @@ bool success = DoubleDouble.TryParse("1.25e-2".AsSpan(), CultureInfo.InvariantCu
The complete logarithmic and exponential families on `DoubleDouble`, and all
three `DoubleDouble.Pow` overloads are implemented.
Generic-math interfaces beyond `ISignedNumber`, `IFloatingPointConstants`,
Generic-math interfaces beyond `INumber`, `ISignedNumber`, `IFloatingPointConstants`,
`IRootFunctions`, `IExponentialFunctions`, `ILogarithmicFunctions`, and `IPowerFunctions`, additional text formats/shortest-
round-trip formatting, and non-arithmetic performance benchmarks remain deferred.
Replacing allocating arithmetic boundary fallbacks is also deferred; the current