fix: negative zero leaked a "-" through Display
val < 0 never clears the sign bit on -0.0 (IEEE 754: -0.0 < 0 is false), so strconv.FormatFloat printed "-0" and normalizeInt passed the leading "-" straight through as an ordinary literal character. Switch to math.Abs(num), which does clear it. Pinned with TestDisplayNegativeZero: -0.0 under "0.00" -> "0.00", under General -> "0". Reachable in practice from a formula like =0*-1. Found in code review (fix round 1).
2 files changed,
+23
−4
internal/sheets/format/format.go+6 −4internal/sheets/format/rounding_test.go+17 −0
Intent
diff --git a/internal/sheets/format/format.go b/internal/sheets/format/format.go| index aa30868..6e2e544 100644 |
| --- a/internal/sheets/format/format.go |
| +++ b/internal/sheets/format/format.go |
| @@ -47,6 +47,7 @@ |
| package format |
| import ( |
| + "math" |
| "strings" |
| "amadan.net/oficina/sheets/internal/sheets/calc" |
| @@ -104,10 +105,11 @@ func displayNumber(num float64, numFmt string) string { |
| } |
| sections := splitSections(code) |
| sec, negate := chooseNumberSection(sections, num) |
| - val := num |
| - if val < 0 { |
| - val = -val |
| - } |
| + // math.Abs, not "if val < 0 { val = -val }": that guard leaves |
| + // negative zero (-0.0 < 0 is false under IEEE 754) unclipped, and |
| + // strconv.FormatFloat prints it as "-0" — a leaked sign on a cell |
| + // that should read plain "0.00". math.Abs clears the sign bit. |
| + val := math.Abs(num) |
| out, ok := renderSection(sec, val) |
| if !ok { |
| out, _ = renderSection("General", val) |
diff --git a/internal/sheets/format/rounding_test.go b/internal/sheets/format/rounding_test.go| index 995f22b..df4d4aa 100644 |
| --- a/internal/sheets/format/rounding_test.go |
| +++ b/internal/sheets/format/rounding_test.go |
| @@ -1,6 +1,7 @@ |
| package format |
| import ( |
| + "math" |
| "testing" |
| "time" |
| @@ -28,6 +29,22 @@ func TestDisplayRoundsHalfAwayFromZero(t *testing.T) { |
| } |
| } |
| +// Negative zero (-0.0 < 0 is false under IEEE 754) must not leak a "-" |
| +// through to the display string. Reachable in practice from a formula |
| +// like =0*-1, which calc does not normalise away. |
| +func TestDisplayNegativeZero(t *testing.T) { |
| + negZero := math.Copysign(0, -1) |
| + if !math.Signbit(negZero) { |
| + t.Fatal("test setup: math.Copysign(0, -1) did not produce a signed zero") |
| + } |
| + if got := Display(numValue(negZero), "0.00"); got != "0.00" { |
| + t.Errorf("Display(-0.0, %q) = %q, want %q", "0.00", got, "0.00") |
| + } |
| + if got := Display(numValue(negZero), "General"); got != "0" { |
| + t.Errorf("Display(-0.0, %q) = %q, want %q", "General", got, "0") |
| + } |
| +} |
| + |
| // Every ErrKind must display as its Excel text under any number format, |
| // not just #DIV/0! under one format — the single case the brief pins. |
| func TestDisplayEveryErrorIgnoresNumberFormat(t *testing.T) { |