diff --git a/.editorconfig b/.editorconfig
index 8b76ca3..cb720e7 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -67,6 +67,8 @@ dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
# Parentheses preferences
+# Keep explicit grouping for readable mathematical expressions, even when redundant.
+dotnet_diagnostic.IDE0047.severity = none
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
diff --git a/.gitignore b/.gitignore
index 916c2f4..4f4b75d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
review-*
*.worktrees
+.hermes
# User-specific files
*.rsuser
diff --git a/0-source/Just.PreciseMath/DoubleDouble.cs b/0-source/Just.PreciseMath/DoubleDouble.cs
index ff3752f..17f03ed 100644
--- a/0-source/Just.PreciseMath/DoubleDouble.cs
+++ b/0-source/Just.PreciseMath/DoubleDouble.cs
@@ -3,10 +3,9 @@ namespace Just.PreciseMath;
///
/// Represents higher precision floating point type
///
-///
-/// Constructs new DoubleDouble from given low and high components
-///
-public readonly struct DoubleDouble : IEquatable, IEqualityOperators
+public readonly struct DoubleDouble :
+ IEquatable,
+ IEqualityOperators
{
internal readonly double _high;
internal readonly double _low;
@@ -18,6 +17,14 @@ public readonly struct DoubleDouble : IEquatable, IEqualityOperato
_low = low;
}
+ ///
+ /// Constructs new DoubleDouble from a given double.
+ ///
+ /// Initial high component
+ public DoubleDouble(double high) : this(high, 0.0)
+ {
+ }
+
#region Static constants
///
/// Represents a value that is not a number (NaN).
@@ -31,6 +38,18 @@ public readonly struct DoubleDouble : IEquatable, IEqualityOperato
/// Represents a zero value.
///
public static DoubleDouble Zero => new();
+ ///
+ /// Represents the ratio of the circumference of a circle to its diameter, specified by the constant, π.
+ ///
+ public static DoubleDouble PI => new(3.141592653589793, 1.2246467991473532e-16);
+ ///
+ /// Represents the natural logarithmic base, specified by the constant, e.
+ ///
+ public static DoubleDouble E => new(2.718281828459045, 1.4456468917292502e-16);
+ ///
+ /// Represents the natural logarithm of value 2.
+ ///
+ public static DoubleDouble LN2 => new(0.6931471805599453, 2.3190468138462996e-17);
#endregion
///
diff --git a/0-source/Just.PreciseMath/PreciseMathHelper.cs b/0-source/Just.PreciseMath/PreciseMathHelper.cs
new file mode 100644
index 0000000..7265b1d
--- /dev/null
+++ b/0-source/Just.PreciseMath/PreciseMathHelper.cs
@@ -0,0 +1,42 @@
+namespace Just.PreciseMath;
+
+internal static class PreciseMathHelper
+{
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static (double Res, double Err) TwoAdd(double a, double b)
+ {
+ double r = a + b;
+ double t = r - a;
+
+ return (r, (a - (r - t)) + (b - t));
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static (double Res, double Err) TwoQuickAdd(double a, double b)
+ {
+ double r = a + b;
+
+ return (r, b - (r - a));
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static (double Res, double Err) TwoSubstract(double a, double b)
+ {
+ double r = a - b;
+ double t = r - a;
+
+ return (r, (a - (r - t)) - (b + t));
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static (double Res, double Err) TwoMultiply(double a, double b)
+ {
+ double r = a * b;
+
+ return (r, Math.FusedMultiplyAdd(a, b, -r));
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static (double Res, double Err) TwoSuare(double a)
+ {
+ double r = a * a;
+
+ return (r, Math.FusedMultiplyAdd(a, a, -r));
+ }
+}
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..e775bd6
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,159 @@
+# Agent guidelines
+
+These rules apply throughout this repository. Keep purpose, status, usage, and
+development instructions in `README.md`; keep agent workflow rules here. Neither
+file is a session log.
+
+## Scope and repository map
+
+This is a WIP .NET double-double arithmetic library, not arbitrary-precision math.
+Do not claim API completeness or accuracy beyond tested contracts.
+
+- `0-source/Just.PreciseMath/`: library implementation and package metadata.
+- `1-tests/Just.PreciseMath.Tests/`: xUnit v3 tests, Shouldly assertions, and
+ Microsoft.Testing.Platform (MTP) with `coverlet.MTP` coverage.
+- `2-benchmarks/Just.PreciseMath.Benchmarks/`: source-free BenchmarkDotNet library
+ scaffold, not yet a runnable benchmark suite.
+- Root `Directory.Build.props` holds shared settings. Each numbered directory's
+ props explicitly imports it; preserve this import chain.
+- `review-legacy/`: optional local reference material, excluded by `.gitignore`.
+ Start with `review-legacy/Review-Revisited-DoubleDouble.md` when present. The code
+ and proposed fixes contain known defects: reproduce findings against current
+ code rather than treating them as correctness oracles. Do not compile, copy
+ wholesale, or force-add these files. If absent, proceed without them; do not
+ invent their contents or make validation depend on them.
+- Use the ignored, repository-local `.hermes/` directory for plans, checklists,
+ investigation notes, and handoff context. Create or update notes as useful;
+ keep them concise and revalidate them against current files. Optional
+ `.hermes/project-context.md` holds setup and review background. Do not store
+ secrets, force-add this directory, or make builds or tests depend on it.
+
+## Working rules
+
+- Check both staged and unstaged changes before starting. Preserve the user's
+ work and index; do not stage, commit, push, publish packages, or rewrite history
+ without an explicit request. Do not read or expose credentials.
+- Read definitions, callers, tests, and relevant configuration before editing.
+ Limit changes to the request; do not implement the roadmap or repair unrelated WIP code.
+- Reproduce numerical bugs with a failing regression test before fixing them.
+ For new behavior, define the contract and test cases before implementation.
+ Exercise sibling overloads and operand orders that can share the defect.
+- Do not weaken analyzers, nullable checks, warnings-as-errors, or assertions to
+ get a green build. Explain necessary policy changes before making them.
+- Keep dependencies minimal. Check existing references, compatibility, and current
+ stable releases before adding or upgrading packages.
+
+## C# conventions
+
+Follow `.editorconfig`, not incidental style in unfinished code.
+
+- Use file-scoped namespaces, explicit types rather than `var`, and block-bodied
+ methods. Preserve the configured expression-bodied property/accessor preferences.
+- Use `_camelCase` for non-public instance fields, including internal fields;
+ `s_camelCase` for non-public mutable static fields; PascalCase for constants
+ and static readonly fields. Do not rename internal fields to remove underscores.
+- Preserve parentheses that make mathematical grouping readable. `IDE0047` is
+ intentionally disabled; do not re-enable it or remove grouping as style cleanup.
+- Document public APIs and non-obvious numerical preconditions. Explain algorithms,
+ error behavior, and range constraints rather than narrating syntax.
+- Tests and benchmarks already have internal access through `InternalsVisibleTo`;
+ do not widen the public API merely to make a helper testable.
+
+## Numerical correctness
+
+- Treat floating-point evaluation order as part of the algorithm. Do not reassociate
+ expressions, discard residuals, replace fused multiply-add with multiply-plus-add,
+ or simplify error-free transforms without justification and regression tests.
+ Mathematically equivalent formulas can round differently.
+- State and verify algorithm preconditions, especially magnitude ordering for
+ quick-sum transforms, normalization assumptions, and overflow/underflow limits.
+- The internal two-component `DoubleDouble` constructor currently does not normalize.
+ Do not assume arbitrary pairs are canonical. Establish the intended normalization,
+ NaN, infinity, and signed-zero contracts before changing construction, equality,
+ hashing, ordering, or classification; keep those operations consistent.
+- For affected operations, cover cancellation, widely separated magnitudes, zero
+ and signed zero, subnormals, extreme finite values, infinities, and NaNs. Check
+ intermediate overflow/underflow even when the final result is representable.
+- Derive expected values from independent high-precision references or exact
+ binary/rational cases, never from the implementation under test. Record the
+ reference source or reproducible derivation and justify tolerances.
+- Do not collapse both components to `double` or `decimal` to validate double-double
+ accuracy: that can discard precisely the bits being tested. Use component-aware
+ or higher-precision comparisons, and inspect sign bits when testing signed zero.
+- Smoke tests, coverage percentages, and benchmark output are not proof of
+ numerical correctness.
+
+## Build and verification
+
+Run commands from the repository root. Read SDK and runner selection from
+`global.json`; read framework, language, and dependency versions from project and
+props files rather than duplicating version pins here.
+
+For documentation-only changes, validate referenced paths and any new or changed
+commands, then run `git diff --check`.
+
+Report commands actually run, their outcomes, and anything not verified. Distinguish
+pre-existing failures from failures introduced by the change.
+
+### Code or build changes
+
+Use targeted tests while iterating, then run the full sequence:
+
+```sh
+dotnet restore Just.PreciseMath.slnx --locked-mode
+dotnet build Just.PreciseMath.slnx -c Release --no-restore
+dotnet test --solution Just.PreciseMath.slnx -c Release --no-build --minimum-expected-tests 1
+dotnet format Just.PreciseMath.slnx --verify-no-changes --no-restore
+git diff --check
+```
+
+Use verify-only formatting first. Report unrelated existing violations separately
+and validate the changed files; do not reformat the repository to hide failures.
+
+### Test or coverage pipeline changes
+
+After the code/build checks, also run:
+
+```sh
+dotnet test --solution Just.PreciseMath.slnx -c Release --no-build --minimum-expected-tests 1 --report-xunit-trx --coverlet --coverlet-output-format cobertura --coverlet-include "[Just.PreciseMath]*"
+```
+
+Keep MTP-native options and the empty-suite failure. Do not substitute VSTest
+collector commands or remove compile assets from `coverlet.MTP`; generated MTP
+registration requires them. Inspect actual TRX and Cobertura output, including
+failed-test behavior when changing report collection.
+
+### Packaging changes
+
+After the code/build checks, run:
+
+```sh
+dotnet pack 0-source/Just.PreciseMath/Just.PreciseMath.csproj -c Release --no-build
+```
+
+Inspect package metadata and the included `README.md` and `LICENSE`. Do not publish.
+
+### Dependency changes
+
+For intentional dependency changes, run `dotnet restore --force-evaluate` and review
+all affected `packages.lock.json` files before the locked restore above. Include
+lock-file updates in the change; never regenerate them to bypass unexpected restore failures.
+
+## CI and benchmarks
+
+- `.gitea/workflows/test-dotnet.yaml` is the CI source of truth. Keep reports
+ artifact-only: no badges, publication branches, or repository-write publishing jobs.
+- Preserve upload-on-failure and both report globs: `1-tests/**/TestResults/**/*.trx`
+ and `1-tests/**/TestResults/**/*cobertura*.xml`. Cobertura filenames can be timestamped;
+ `*.cobertura.xml` alone will miss them.
+- Cache NuGet packages, not `bin/` or `obj/`; restore remains necessary on a cache hit.
+ A local restore does not prove CI cache reuse or remote artifact upload succeeded.
+- Before upgrading actions, check each action's required Node runtime against both
+ the Gitea runner and job image. A recent runner can still launch an old Node image;
+ `ubuntu-latest` is a configured label, not a guarantee of GitHub's environment.
+- Do not add an empty benchmark job. When benchmarks are introduced, add an executable
+ entry point and cases, then smoke-run them in Release with BenchmarkDotNet's Dry job.
+ Require discovered/executed cases and verify that benchmark failures fail CI.
+- Keep smoke validation separate from performance measurement. Do not gate timing
+ regressions on a shared runner or compare coverage-instrumented measurements.
+ Use controlled, repeatable baseline/candidate runs before proposing performance gates.
diff --git a/README.md b/README.md
index 23d117c..cb7292b 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,12 @@
# Just.PreciseMath
-**WIP — initial library implementation and one smoke test; numerical correctness is not yet validated.**
+Extended-precision floating-point arithmetic for .NET using double-double
+representations: a high/low pair of `double` values. The goal is to retain more
+precision than a single `double` while using a fixed-size representation,
+rather than arbitrary-precision arithmetic.
-A .NET 10 library being developed for extended-precision floating-point arithmetic
-using double-double representations (a high/low pair of `double` values), with
-common mathematical functions. This is fixed extended precision, not arbitrary
-precision; accuracy guarantees and the public API are not settled.
+> **Work in progress.** The public API is incomplete and may change. Numerical
+> accuracy has not been validated, and the library is not ready for production use.
## Planned scope
@@ -13,64 +14,35 @@ precision; accuracy guarantees and the public API are not settled.
- Common functions including `Abs`, `Sqrt`, `Pow`, `Exp`, and `Log`.
- Correctness tests against higher-precision references and performance benchmarks.
-The [consolidated legacy review](review-legacy/Review-Revisited-DoubleDouble.md)
-is the starting point. Priorities are normalization and special-value contracts
-(NaN, infinities, signed zero), corrected constants and scalar subtraction,
-consistent comparisons, precision-preserving conversions/formatting, and
-range-safe arithmetic and transcendental functions, including cancellation and
-subnormal cases. The files in `review-legacy/` contain known defects: they are
-reference material only, are not compiled, and should not be used in production.
+These are development goals, not a list of currently supported features.
-## Layout
+## Build and test
-- `0-source/Just.PreciseMath/` — library and package metadata.
-- `1-tests/Just.PreciseMath.Tests/` — xUnit v3, Shouldly, and MTP-native Coverlet.
-- `2-benchmarks/Just.PreciseMath.Benchmarks/` — BenchmarkDotNet scaffold.
-- `review-legacy/` — original implementation and numerical reviews.
-
-## Development
-
-Use the .NET 10 SDK selected by `global.json` (10.0.1xx, latest installed patch).
-Shared settings enable nullable analysis, .NET 10 recommended analyzers, build-time
-code-style checks, and warnings as errors. `.editorconfig` retains advisory style
-preferences alongside explicitly enforced warning/error rules.
+Requires the .NET 10 SDK in the `10.0.1xx` feature band, as selected by `global.json`.
+Run from the repository root:
```sh
-dotnet restore Just.PreciseMath.slnx
+dotnet restore Just.PreciseMath.slnx --locked-mode
dotnet build Just.PreciseMath.slnx -c Release --no-restore
-dotnet test --solution Just.PreciseMath.slnx -c Release --no-build
+dotnet test --solution Just.PreciseMath.slnx -c Release --no-build --minimum-expected-tests 1
dotnet format Just.PreciseMath.slnx --verify-no-changes --no-restore
```
-Commit each project's `packages.lock.json` with dependency changes. CI caches
-NuGet packages using those lock files and always runs `dotnet restore --locked-mode`,
-including on cache hits. After intentional dependency updates, run
-`dotnet restore --force-evaluate` and review the updated lock files before committing.
-Gitea's runner cache must be reachable from the job container, with persistent
-storage if the runner itself is recreated. No build outputs are cached.
+Test results and coverage reports are available in the `test-results` artifact
+on CI workflow runs.
-The initial smoke test checks that `DoubleDouble.One` exposes high and low
-components of `1.0` and `0.0`. It exercises the test/coverage pipeline, not the
-numerical accuracy of the planned library.
+## Project structure
-CI collects TRX test results and Cobertura coverage for `Just.PreciseMath` only,
-excluding test dependencies, and requires at least one test. The `test-results`
-artifact contains TRX test results and timestamped Cobertura XML coverage reports,
-retained for three days.
-Open the workflow run in Gitea Actions and download that artifact. Upload is
-attempted even when tests fail; no badges or repository-write token are needed.
+- `0-source/Just.PreciseMath/`: library implementation.
+- `1-tests/Just.PreciseMath.Tests/`: unit tests.
+- `2-benchmarks/Just.PreciseMath.Benchmarks/`: reserved for performance benchmarks.
-To generate the same reports locally:
+## Contributing
-```sh
-dotnet test --solution Just.PreciseMath.slnx -c Release --no-build --minimum-expected-tests 1 --report-xunit-trx --coverlet --coverlet-output-format cobertura --coverlet-include "[Just.PreciseMath]*"
-```
+Follow `.editorconfig` and include regression tests with numerical changes.
+Explain the algorithm's assumptions, the source of reference values, and any
+error tolerances. Include updated `packages.lock.json` files with dependency changes.
-Reports are written under each test project's `bin/Release/net10.0/TestResults/`.
-The benchmark project deliberately builds as a library without source files;
-add an entry point and benchmark cases, then change `OutputType` to `Exe` and run
-it in Release without a debugger. No performance results exist yet.
+## License
-Package metadata uses the development version `0.1.0-dev`. Licensed under the
-[MIT License](LICENSE), included in the package.
-Do not publish this scaffold as a usable numerical library.
+Licensed under the [MIT License](LICENSE).