This commit is contained in:
@@ -67,6 +67,8 @@ dotnet_style_predefined_type_for_locals_parameters_members = true:silent
|
|||||||
dotnet_style_predefined_type_for_member_access = true:silent
|
dotnet_style_predefined_type_for_member_access = true:silent
|
||||||
|
|
||||||
# Parentheses preferences
|
# 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_arithmetic_binary_operators = always_for_clarity:silent
|
||||||
dotnet_style_parentheses_in_other_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
|
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
review-*
|
review-*
|
||||||
*.worktrees
|
*.worktrees
|
||||||
|
.hermes
|
||||||
|
|
||||||
# User-specific files
|
# User-specific files
|
||||||
*.rsuser
|
*.rsuser
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ namespace Just.PreciseMath;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents higher precision floating point type
|
/// Represents higher precision floating point type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
public readonly struct DoubleDouble :
|
||||||
/// Constructs new DoubleDouble from given low and high components
|
IEquatable<DoubleDouble>,
|
||||||
/// </remarks>
|
IEqualityOperators<DoubleDouble, DoubleDouble, bool>
|
||||||
public readonly struct DoubleDouble : IEquatable<DoubleDouble>, IEqualityOperators<DoubleDouble, DoubleDouble, bool>
|
|
||||||
{
|
{
|
||||||
internal readonly double _high;
|
internal readonly double _high;
|
||||||
internal readonly double _low;
|
internal readonly double _low;
|
||||||
@@ -18,6 +17,14 @@ public readonly struct DoubleDouble : IEquatable<DoubleDouble>, IEqualityOperato
|
|||||||
_low = low;
|
_low = low;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs new DoubleDouble from a given double.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="high">Initial high component</param>
|
||||||
|
public DoubleDouble(double high) : this(high, 0.0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
#region Static constants
|
#region Static constants
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a value that is not a number (NaN).
|
/// Represents a value that is not a number (NaN).
|
||||||
@@ -31,6 +38,18 @@ public readonly struct DoubleDouble : IEquatable<DoubleDouble>, IEqualityOperato
|
|||||||
/// Represents a zero value.
|
/// Represents a zero value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static DoubleDouble Zero => new();
|
public static DoubleDouble Zero => new();
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the ratio of the circumference of a circle to its diameter, specified by the constant, π.
|
||||||
|
/// </summary>
|
||||||
|
public static DoubleDouble PI => new(3.141592653589793, 1.2246467991473532e-16);
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the natural logarithmic base, specified by the constant, e.
|
||||||
|
/// </summary>
|
||||||
|
public static DoubleDouble E => new(2.718281828459045, 1.4456468917292502e-16);
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the natural logarithm of value 2.
|
||||||
|
/// </summary>
|
||||||
|
public static DoubleDouble LN2 => new(0.6931471805599453, 2.3190468138462996e-17);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
# Just.PreciseMath
|
# 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
|
> **Work in progress.** The public API is incomplete and may change. Numerical
|
||||||
using double-double representations (a high/low pair of `double` values), with
|
> accuracy has not been validated, and the library is not ready for production use.
|
||||||
common mathematical functions. This is fixed extended precision, not arbitrary
|
|
||||||
precision; accuracy guarantees and the public API are not settled.
|
|
||||||
|
|
||||||
## Planned scope
|
## 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`.
|
- Common functions including `Abs`, `Sqrt`, `Pow`, `Exp`, and `Log`.
|
||||||
- Correctness tests against higher-precision references and performance benchmarks.
|
- Correctness tests against higher-precision references and performance benchmarks.
|
||||||
|
|
||||||
The [consolidated legacy review](review-legacy/Review-Revisited-DoubleDouble.md)
|
These are development goals, not a list of currently supported features.
|
||||||
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.
|
|
||||||
|
|
||||||
## Layout
|
## Build and test
|
||||||
|
|
||||||
- `0-source/Just.PreciseMath/` — library and package metadata.
|
Requires the .NET 10 SDK in the `10.0.1xx` feature band, as selected by `global.json`.
|
||||||
- `1-tests/Just.PreciseMath.Tests/` — xUnit v3, Shouldly, and MTP-native Coverlet.
|
Run from the repository root:
|
||||||
- `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.
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
dotnet restore Just.PreciseMath.slnx
|
dotnet restore Just.PreciseMath.slnx --locked-mode
|
||||||
dotnet build Just.PreciseMath.slnx -c Release --no-restore
|
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
|
dotnet format Just.PreciseMath.slnx --verify-no-changes --no-restore
|
||||||
```
|
```
|
||||||
|
|
||||||
Commit each project's `packages.lock.json` with dependency changes. CI caches
|
Test results and coverage reports are available in the `test-results` artifact
|
||||||
NuGet packages using those lock files and always runs `dotnet restore --locked-mode`,
|
on CI workflow runs.
|
||||||
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.
|
|
||||||
|
|
||||||
The initial smoke test checks that `DoubleDouble.One` exposes high and low
|
## Project structure
|
||||||
components of `1.0` and `0.0`. It exercises the test/coverage pipeline, not the
|
|
||||||
numerical accuracy of the planned library.
|
|
||||||
|
|
||||||
CI collects TRX test results and Cobertura coverage for `Just.PreciseMath` only,
|
- `0-source/Just.PreciseMath/`: library implementation.
|
||||||
excluding test dependencies, and requires at least one test. The `test-results`
|
- `1-tests/Just.PreciseMath.Tests/`: unit tests.
|
||||||
artifact contains TRX test results and timestamped Cobertura XML coverage reports,
|
- `2-benchmarks/Just.PreciseMath.Benchmarks/`: reserved for performance benchmarks.
|
||||||
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.
|
|
||||||
|
|
||||||
To generate the same reports locally:
|
## Contributing
|
||||||
|
|
||||||
```sh
|
Follow `.editorconfig` and include regression tests with numerical changes.
|
||||||
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]*"
|
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/`.
|
## License
|
||||||
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.
|
|
||||||
|
|
||||||
Package metadata uses the development version `0.1.0-dev`. Licensed under the
|
Licensed under the [MIT License](LICENSE).
|
||||||
[MIT License](LICENSE), included in the package.
|
|
||||||
Do not publish this scaffold as a usable numerical library.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user