diff --git a/.gitea/workflows/test-dotnet.yaml b/.gitea/workflows/test-dotnet.yaml
index cc7329c..e1dd9f2 100644
--- a/.gitea/workflows/test-dotnet.yaml
+++ b/.gitea/workflows/test-dotnet.yaml
@@ -33,6 +33,8 @@ jobs:
uses: actions/setup-dotnet@v6
with:
global-json-file: global.json
+ # Install .NET 8 alongside the build SDK selected by global.json.
+ dotnet-version: '8.0.x'
cache: true
cache-dependency-path: '**/packages.lock.json'
diff --git a/0-source/Directory.Build.props b/0-source/Directory.Build.props
index 3eb0841..87857c8 100644
--- a/0-source/Directory.Build.props
+++ b/0-source/Directory.Build.props
@@ -3,6 +3,7 @@
+ net8.0;net10.0
true
true
0.1.0
diff --git a/0-source/Just.PreciseMath/packages.lock.json b/0-source/Just.PreciseMath/packages.lock.json
index 4a91a8c..2b911b5 100644
--- a/0-source/Just.PreciseMath/packages.lock.json
+++ b/0-source/Just.PreciseMath/packages.lock.json
@@ -1,6 +1,7 @@
{
"version": 1,
"dependencies": {
- "net10.0": {}
+ "net10.0": {},
+ "net8.0": {}
}
}
\ No newline at end of file
diff --git a/1-tests/Directory.Build.props b/1-tests/Directory.Build.props
index a9288b0..1d1b9ba 100644
--- a/1-tests/Directory.Build.props
+++ b/1-tests/Directory.Build.props
@@ -3,6 +3,7 @@
+ net8.0;net10.0
Exe
false
diff --git a/1-tests/Just.PreciseMath.Tests/GenericConversionTests.cs b/1-tests/Just.PreciseMath.Tests/GenericConversionTests.cs
index 189306a..e32a79d 100644
--- a/1-tests/Just.PreciseMath.Tests/GenericConversionTests.cs
+++ b/1-tests/Just.PreciseMath.Tests/GenericConversionTests.cs
@@ -279,7 +279,9 @@ public sealed class GenericConversionTests
Should.Throw(() => decimal.CreateChecked(new DoubleDouble(double.PositiveInfinity)));
decimal.CreateSaturating(new DoubleDouble(double.PositiveInfinity)).ShouldBe(decimal.MaxValue);
decimal.CreateTruncating(DoubleDouble.NaN).ShouldBe(0m);
- int.CreateSaturating(DoubleDouble.NaN).ShouldBe(0);
+ // Nonfinite integer conversions follow the destination's binary64 policy,
+ // which can differ between runtimes (for example, .NET 8 and .NET 10).
+ int.CreateSaturating(DoubleDouble.NaN).ShouldBe(int.CreateSaturating(double.NaN));
int.CreateTruncating(new DoubleDouble(double.PositiveInfinity)).ShouldBe(int.MaxValue);
Should.Throw(() => int.CreateChecked(DoubleDouble.NaN));
}
diff --git a/1-tests/Just.PreciseMath.Tests/packages.lock.json b/1-tests/Just.PreciseMath.Tests/packages.lock.json
index 714fb55..5065436 100644
--- a/1-tests/Just.PreciseMath.Tests/packages.lock.json
+++ b/1-tests/Just.PreciseMath.Tests/packages.lock.json
@@ -262,6 +262,288 @@
"just.precisemath": {
"type": "Project"
}
+ },
+ "net8.0": {
+ "coverlet.MTP": {
+ "type": "Direct",
+ "requested": "[10.0.1, )",
+ "resolved": "10.0.1",
+ "contentHash": "VR4/Y0Arr8cdIKhYE5dZCI2XU1ShCHZEAgm+LjokTThl1vzy9scqq8NDLETok5AchMTtsUQqcAU81N1cgHUclg==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration": "10.0.8",
+ "Microsoft.Extensions.Configuration.Json": "10.0.8",
+ "Microsoft.Extensions.DependencyInjection": "10.0.8",
+ "Microsoft.Testing.Platform": "2.2.2"
+ }
+ },
+ "Shouldly": {
+ "type": "Direct",
+ "requested": "[4.3.0, )",
+ "resolved": "4.3.0",
+ "contentHash": "sDetrWXrl6YXZ4HeLsdBoNk3uIa7K+V4uvIJ+cqdRa5DrFxeTED7VkjoxCuU1kJWpUuBDZz2QXFzSxBtVXLwRQ==",
+ "dependencies": {
+ "DiffEngine": "11.3.0",
+ "EmptyFiles": "4.4.0"
+ }
+ },
+ "xunit.v3": {
+ "type": "Direct",
+ "requested": "[4.0.1, )",
+ "resolved": "4.0.1",
+ "contentHash": "KpDYf6jFjYfOchuRJOAXPOpwN03hLE1AF4kWzCUSnhr8JZMiGH4BT1YidCcm6BE1H4E90GyJzuKoCwXbg9PXmA==",
+ "dependencies": {
+ "xunit.v3.mtp-v2": "[4.0.1]"
+ }
+ },
+ "DiffEngine": {
+ "type": "Transitive",
+ "resolved": "11.3.0",
+ "contentHash": "k0ZgZqd09jLZQjR8FyQbSQE86Q7QZnjEzq1LPHtj1R2AoWO8sjV5x+jlSisL7NZAbUOI4y+7Bog8gkr9WIRBGw==",
+ "dependencies": {
+ "EmptyFiles": "4.4.0",
+ "System.Management": "6.0.1"
+ }
+ },
+ "EmptyFiles": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "gwJEfIGS7FhykvtZoscwXj/XwW+mJY6UbAZk+qtLKFUGWC95kfKXnj8VkxsZQnWBxJemM/q664rGLN5nf+OHZw=="
+ },
+ "Microsoft.ApplicationInsights": {
+ "type": "Transitive",
+ "resolved": "2.23.0",
+ "contentHash": "nWArUZTdU7iqZLycLKWe0TDms48KKGE6pONH2terYNa8REXiqixrMOkf1sk5DHGMaUTqONU2YkS4SAXBhLStgw=="
+ },
+ "Microsoft.Bcl.AsyncInterfaces": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg=="
+ },
+ "Microsoft.Extensions.Configuration": {
+ "type": "Transitive",
+ "resolved": "10.0.8",
+ "contentHash": "ehZcoPbjzWzS4XFvuz7R3V55SmpdkyMqFURLH3yXaN9NtXd9tR6CGB7pd49HYtCkenl+G7ctXSFLhNI08xLfRg==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.8",
+ "Microsoft.Extensions.Primitives": "10.0.8"
+ }
+ },
+ "Microsoft.Extensions.Configuration.Abstractions": {
+ "type": "Transitive",
+ "resolved": "10.0.8",
+ "contentHash": "I63esIFbL3h5pSt7gXpXOlmcwDmYBUoYNEglKfDPFUqtYvSV84f2l28hO2lfVXsV0wdlplgAM7IVz16matapSg==",
+ "dependencies": {
+ "Microsoft.Extensions.Primitives": "10.0.8"
+ }
+ },
+ "Microsoft.Extensions.Configuration.FileExtensions": {
+ "type": "Transitive",
+ "resolved": "10.0.8",
+ "contentHash": "1g9mzuu8gIHkjYb0jLxOTQVl/QDG5nn0b0JzgT/gbgNKr6gXZzxOHRAsdYRc1eDApB7LdHR8uK5vQrNjIQdRrQ==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration": "10.0.8",
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.8",
+ "Microsoft.Extensions.FileProviders.Abstractions": "10.0.8",
+ "Microsoft.Extensions.FileProviders.Physical": "10.0.8",
+ "Microsoft.Extensions.Primitives": "10.0.8"
+ }
+ },
+ "Microsoft.Extensions.Configuration.Json": {
+ "type": "Transitive",
+ "resolved": "10.0.8",
+ "contentHash": "KLtAZ6A38s1pIfCO2ns6aG14NNGMYNZ4PBYfFK4M+R4A+xuSc6oklhqDcpHZxvDpyBWeFtR5C8iQBw2ng8tUHQ==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration": "10.0.8",
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.8",
+ "Microsoft.Extensions.Configuration.FileExtensions": "10.0.8",
+ "Microsoft.Extensions.FileProviders.Abstractions": "10.0.8",
+ "System.Text.Json": "10.0.8"
+ }
+ },
+ "Microsoft.Extensions.DependencyInjection": {
+ "type": "Transitive",
+ "resolved": "10.0.8",
+ "contentHash": "daf62xHIrq8pnE709hgaZZN9tSam9TGGepWe1+bE6V3GEuVwJiMs6ib+38lfMCyAJAHiX0vapxBhsuMSV7U+cg==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.8"
+ }
+ },
+ "Microsoft.Extensions.DependencyInjection.Abstractions": {
+ "type": "Transitive",
+ "resolved": "10.0.8",
+ "contentHash": "21nbDV60SRPWGIivsyl6lqBeEJNG1sginhhfWgRrr3Ais7aQ12To25OAHQxgoiJkjqy1aQ6RxpZBGYuTi7Ge6A=="
+ },
+ "Microsoft.Extensions.FileProviders.Abstractions": {
+ "type": "Transitive",
+ "resolved": "10.0.8",
+ "contentHash": "U+oquaPxFdY8lYeEIWO/AD7jDIl9sPW6aVWMQRHU/pZ/SWpLcOrAj2fcLe1HwXl4sYw1ONI56K/eELT3xr4RRQ==",
+ "dependencies": {
+ "Microsoft.Extensions.Primitives": "10.0.8"
+ }
+ },
+ "Microsoft.Extensions.FileProviders.Physical": {
+ "type": "Transitive",
+ "resolved": "10.0.8",
+ "contentHash": "GkPvQe6IdidLu6Q3Lw6+B8NJpW8feW8czZ5mBKt5rXM/x8MvZfEp5WvAsjznzDGd23chIDrW0b2mmt+ScnEgiw==",
+ "dependencies": {
+ "Microsoft.Extensions.FileProviders.Abstractions": "10.0.8",
+ "Microsoft.Extensions.FileSystemGlobbing": "10.0.8",
+ "Microsoft.Extensions.Primitives": "10.0.8"
+ }
+ },
+ "Microsoft.Extensions.FileSystemGlobbing": {
+ "type": "Transitive",
+ "resolved": "10.0.8",
+ "contentHash": "IUQet3SY51xIFcFZKtAB6a54/Zdxs7T3SQ84kJtOD6yeXfZgiOMksACWD5qtTmXGQGFH4QYGBOT0KIO8Uy/dJw=="
+ },
+ "Microsoft.Extensions.Primitives": {
+ "type": "Transitive",
+ "resolved": "10.0.8",
+ "contentHash": "OBPo4nYhMyIbtueoC10CBm6AGAbo/A9IV8QQ/6ryZS7VvmqpGT7hunazeHLxFawRzn3oLOq4jhqhpBX4tfswWQ=="
+ },
+ "Microsoft.Testing.Extensions.Telemetry": {
+ "type": "Transitive",
+ "resolved": "2.4.0",
+ "contentHash": "JeP1RFqBa11fWmBk8xEfZcMKr4rxWSyI6OZ+659V069CaMkTEOQBW2UdSSeNz3absOsygcn7JJkzerC4LGnZ9w==",
+ "dependencies": {
+ "Microsoft.ApplicationInsights": "2.23.0",
+ "Microsoft.Testing.Platform": "[2.4.0, 3.0.0)"
+ }
+ },
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": {
+ "type": "Transitive",
+ "resolved": "2.4.0",
+ "contentHash": "uRb+4qM42dFDg4kWJZ2kFEcwESNVCRZJjItp5vETorN3rSJGysP617LqYirOcrCYmxg+obPreHMM5JcsNDKtBw==",
+ "dependencies": {
+ "Microsoft.Testing.Platform": "[2.4.0, 3.0.0)"
+ }
+ },
+ "Microsoft.Testing.Platform": {
+ "type": "Transitive",
+ "resolved": "2.4.0",
+ "contentHash": "dp1N3P1ujb0ztwFgqz2o/ItEvq+pm19/AiA+Xq7Zpcy7oPMcxDBZLYGtf0LlU1MveEBJuKVjZhI+LnkfcH/0jQ=="
+ },
+ "Microsoft.Testing.Platform.MSBuild": {
+ "type": "Transitive",
+ "resolved": "2.4.0",
+ "contentHash": "qr5M6h16YHMJLFDcWELFVMMpGte2BUmveBZKT5YoBV+bmuJRPu9bv/Zqke4yQuOEKRNxoAETrG5jr+/6Rnr3Hg==",
+ "dependencies": {
+ "Microsoft.Testing.Platform": "[2.4.0, 3.0.0)"
+ }
+ },
+ "Microsoft.Win32.Registry": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg=="
+ },
+ "System.CodeDom": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA=="
+ },
+ "System.IO.Pipelines": {
+ "type": "Transitive",
+ "resolved": "10.0.8",
+ "contentHash": "STVNTIVd+UrYvSo31D2tOOTs19IThdjGDN14FS3/NZb4PWsVakAg/VT4sq+JYWGP/GtazzvXwebWL6LPBAwnFQ=="
+ },
+ "System.Management": {
+ "type": "Transitive",
+ "resolved": "6.0.1",
+ "contentHash": "10J1D0h/lioojphfJ4Fuh5ZUThT/xOVHdV9roGBittKKNP2PMjrvibEdbVTGZcPra1399Ja3tqIJLyQrc5Wmhg==",
+ "dependencies": {
+ "System.CodeDom": "6.0.0"
+ }
+ },
+ "System.Security.AccessControl": {
+ "type": "Transitive",
+ "resolved": "6.0.1",
+ "contentHash": "IQ4NXP/B3Ayzvw0rDQzVTYsCKyy0Jp9KI6aYcK7UnGVlR9+Awz++TIPCQtPYfLJfOpm8ajowMR09V7quD3sEHw=="
+ },
+ "System.Text.Encodings.Web": {
+ "type": "Transitive",
+ "resolved": "10.0.8",
+ "contentHash": "HvFIJXM/CMTRu6PBQmjukQZ/O32Vx5fOEYZs0kq0OD5s9vJQ19KHLWCVmnfh3gNC7pbYomm06tzOTgJBflr/nQ=="
+ },
+ "System.Text.Json": {
+ "type": "Transitive",
+ "resolved": "10.0.8",
+ "contentHash": "/fysUDkD7oFGaRPoA7IaFs0wRoO3GlwlCNq2P+xWZqxLy1R4cktRSKfMjJDy9ymS4grL7IDVdt8de8L9a0z55Q==",
+ "dependencies": {
+ "System.IO.Pipelines": "10.0.8",
+ "System.Text.Encodings.Web": "10.0.8"
+ }
+ },
+ "xunit.analyzers": {
+ "type": "Transitive",
+ "resolved": "2.1.0",
+ "contentHash": "X7QXEcZQGz0G/HL4HUyK+aAvNa/IMGbOCnFIq4jD/Evktq12xANKwzOUr7b08vCmC1LXu/47qHWOdjm3KfaJ0A=="
+ },
+ "xunit.v3.assert": {
+ "type": "Transitive",
+ "resolved": "4.0.1",
+ "contentHash": "nC7d3cY06Oo7hkdwWPZkBR0Ud75xNhgx0P7i0GmMMZC3puCGlc4szFiT30biH03IN/eDnr8h+nv3A1UD17bk/A=="
+ },
+ "xunit.v3.common": {
+ "type": "Transitive",
+ "resolved": "4.0.1",
+ "contentHash": "Qf25TVdadDQYf9zVxSd7L9RNQhuP0jCUHmo96XTmIKLxGefylIEV63jRbXBPFzdXmV09TCdcurk+AQ9LiWS2Hg==",
+ "dependencies": {
+ "Microsoft.Bcl.AsyncInterfaces": "6.0.0"
+ }
+ },
+ "xunit.v3.core.mtp-v2": {
+ "type": "Transitive",
+ "resolved": "4.0.1",
+ "contentHash": "7qfTfrIfS2wybpSVyRLmhXHbSudD2eNw66LfukixmKsRCTcfvOrLsx0qGL/lObcEh3Z2F4SIXgoFnLIiB8yfjQ==",
+ "dependencies": {
+ "Microsoft.Testing.Extensions.Telemetry": "2.4.0",
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.4.0",
+ "Microsoft.Testing.Platform": "2.4.0",
+ "Microsoft.Testing.Platform.MSBuild": "2.4.0",
+ "xunit.v3.extensibility.core": "[4.0.1]",
+ "xunit.v3.runner.inproc.console": "[4.0.1]"
+ }
+ },
+ "xunit.v3.extensibility.core": {
+ "type": "Transitive",
+ "resolved": "4.0.1",
+ "contentHash": "J0d5OfFcp920nZxCRIvU14+TZhiSPQR0wvqEMZ3MRiJLPu1VmYlcRNkmcSW0i1DwkB81gAHc+fHUQf/cU64MYg==",
+ "dependencies": {
+ "xunit.v3.common": "[4.0.1]"
+ }
+ },
+ "xunit.v3.mtp-v2": {
+ "type": "Transitive",
+ "resolved": "4.0.1",
+ "contentHash": "s88KiWwDDYgOWV3A+ViJiqCe9cLU/Rt6Gb5TfOC7Abv/HKSoj4IHVPcOQk7jPt7HhZHHEts5IuaVpB30eO5B1w==",
+ "dependencies": {
+ "xunit.analyzers": "2.1.0",
+ "xunit.v3.assert": "[4.0.1]",
+ "xunit.v3.core.mtp-v2": "[4.0.1]"
+ }
+ },
+ "xunit.v3.runner.common": {
+ "type": "Transitive",
+ "resolved": "4.0.1",
+ "contentHash": "p9AyfBpj2e5Iws8B57SbLiSngg7mEdvegHInKR4T2cjt2mis9h8htVgCnmu//agZO4zri6p6VQOj5EkHHTy3gA==",
+ "dependencies": {
+ "Microsoft.Win32.Registry": "[5.0.0]",
+ "System.Security.AccessControl": "[6.0.1]",
+ "xunit.v3.common": "[4.0.1]"
+ }
+ },
+ "xunit.v3.runner.inproc.console": {
+ "type": "Transitive",
+ "resolved": "4.0.1",
+ "contentHash": "Hqwfd6ehMIhPmVWUQ2dgmknzuLFTWeyp8ES1q3D4YR5bQVyiXDcaIoaFwqAz2zgLr49WaW4Mz7VVncP7u/Q8/Q==",
+ "dependencies": {
+ "xunit.v3.extensibility.core": "[4.0.1]",
+ "xunit.v3.runner.common": "[4.0.1]"
+ }
+ },
+ "just.precisemath": {
+ "type": "Project"
+ }
}
}
}
\ No newline at end of file
diff --git a/2-benchmarks/Directory.Build.props b/2-benchmarks/Directory.Build.props
index 0fb9729..7fa3b71 100644
--- a/2-benchmarks/Directory.Build.props
+++ b/2-benchmarks/Directory.Build.props
@@ -2,6 +2,7 @@
+ net10.0
false
false
diff --git a/Directory.Build.props b/Directory.Build.props
index 34e4b0c..eeb9548 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,6 +1,5 @@
- net10.0
14.0
enable
enable
diff --git a/README.md b/README.md
index fd2293d..356eed5 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,8 @@ 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.
+The library targets **.NET 8 and .NET 10**.
+
> **Work in progress.** The public API is incomplete and may change. Numerical
> contracts are covered by regression tests, not an exhaustive accuracy
> certification. The library is not ready for production use.
@@ -346,6 +348,10 @@ or retains the low destination-width bits, respectively. Decimal nonchecked outp
clamps out-of-range values and maps NaN to zero. These policies are distinct from
the existing casts and `IConvertible` conversions above.
+Nonfinite integer output follows the destination type's binary64 conversion policy,
+which can differ across runtimes and architectures. For example, saturating NaN to
+`int` yields `int.MinValue` on .NET 8 Linux x64 and zero on .NET 10 Linux x64.
+
Conversions, parsing, and formatting use allocating `BigInteger` intermediates
where needed to preserve precision; no additional dependency is required.
@@ -408,7 +414,10 @@ Replacing allocating arithmetic boundary fallbacks is also deferred; the current
## Build and test
Requires the .NET 10 SDK in the `10.0.1xx` feature band, as selected by `global.json`.
-Run from the repository root:
+Install the .NET 8 runtime as well to run both test targets. The library and tests
+multi-target `net8.0` and `net10.0`; benchmarks remain on `net10.0`. The build SDK
+requirement does not prevent .NET 8 applications from consuming the library.
+Run from the repository root; the test command runs both frameworks:
```sh
dotnet restore Just.PreciseMath.slnx --locked-mode