mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-10-22 14:11:37 -04:00
fix(cutoffs): preserve exact integral scores in strip search
Avoid shortest round-trip decimal text for already-integral doubles. The failing tie regression and the independent 9,163-case exact-integer sweep now keep the fewer-pierce result.
This commit is contained in:
1 parent
57450ecea8
commit
c107ab13f7
2 files changed
+23
-10
No files matched your search
@@ -104,18 +104,17 @@ internal static class AutomaticCutOffStripSearch
|
||||
if (!double.IsFinite(length) || length < 0)
|
||||
throw new ArgumentException("Cut-off segment length must be finite and nonnegative.", nameof(length));
|
||||
var rounded = System.Math.Round(length, 6);
|
||||
// A shortest round-trip decimal may spell a different integer near 1e16
|
||||
// (for example 26833294515438512d prints as 26833294515438510).
|
||||
// An already-integral double is exact; never round it through text.
|
||||
if (rounded == System.Math.Truncate(rounded))
|
||||
return new BigInteger(rounded) * 1_000_000;
|
||||
// Compare sums of the specified decimal-rounded model units, not binary
|
||||
// approximations such as 0.1 + 0.2 versus 0.3. The round-trip string
|
||||
// retains micro-unit digits even at larger (but still fractional) scales.
|
||||
if (rounded < 1e22)
|
||||
{
|
||||
var value = decimal.Parse(rounded.ToString("R", CultureInfo.InvariantCulture),
|
||||
NumberStyles.Float, CultureInfo.InvariantCulture);
|
||||
return new BigInteger(decimal.Round(value * 1_000_000m, 0));
|
||||
}
|
||||
// At this magnitude a double has no fractional model units; avoid a
|
||||
// decimal overflow while retaining an unbounded exact accumulated score.
|
||||
return new BigInteger(rounded) * 1_000_000;
|
||||
// retains micro-unit digits for fractional scores.
|
||||
var value = decimal.Parse(rounded.ToString("R", CultureInfo.InvariantCulture),
|
||||
NumberStyles.Float, CultureInfo.InvariantCulture);
|
||||
return new BigInteger(decimal.Round(value * 1_000_000m, 0));
|
||||
}
|
||||
|
||||
private static int Compare(State left, State right)
|
||||
|
||||
@@ -22,6 +22,20 @@ public class AutomaticCutOffStripSearchTests
|
||||
Assert.Equal(new[] { 20.0, 40.0 }, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IntegralDoubleTie_UsesExactValueBeforePierces()
|
||||
{
|
||||
var first = 10576245042702564d;
|
||||
var second = 16257049472735948d;
|
||||
var combined = 26833294515438512d;
|
||||
Assert.Equal((decimal)first + (decimal)second, (decimal)combined);
|
||||
var result = AutomaticCutOffStripSearch.Solve(0,
|
||||
new[] { new[] { 20.0, 35.0 }, new[] { 40.0, 70.0 } },
|
||||
new[] { 35.0, 70.0 }, 35, 17.5,
|
||||
x => x switch { 20 => (first, 1), 40 => (second, 1), 35 => (combined, 3), _ => (0.0, 0) });
|
||||
Assert.Equal(new[] { 20.0, 40.0 }, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GlobalOptimum_IsBetterThanGreedyFirstLine()
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user