Skip to content

Commit 773c098

Browse files
fable-repo-assist[bot]Copilotgithub-actions[bot]MangelMaxime
authored
fix(js/ts): fix G/g format specifier corrupting exponential notation when trimming trailing zeros (#4587)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Maxime Mangel <me@mangelmaxime.fr>
1 parent 764c383 commit 773c098

3 files changed

Lines changed: 30 additions & 42 deletions

File tree

package-lock.json

Lines changed: 0 additions & 39 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/fable-library-ts/String.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -390,11 +390,23 @@ export function format(str: string | object, ...args: any[]) {
390390
rep = parts.integral + "." + padRight(parts.decimal, precision, "0");
391391
}
392392
break;
393-
case "g": case "G":
393+
case "g": case "G": {
394394
rep = precision != null ? toPrecision(rep, precision) : toPrecision(rep);
395-
// TODO: Check why some numbers are formatted with decimal part
396-
rep = trimEnd(trimEnd(rep, "0"), ".");
395+
// Handle exponential notation: only trim trailing zeros from mantissa, not from exponent.
396+
// .NET G format guarantees an exponent of at least 2 digits with an explicit sign (e.g. "E-07").
397+
const eIdx = rep.indexOf("e");
398+
if (eIdx >= 0) {
399+
const mantissa = trimEnd(trimEnd(rep.slice(0, eIdx), "0"), ".");
400+
const expSign = rep[eIdx + 1]; // toPrecision always emits "+" or "-"
401+
const expDigits = rep.slice(eIdx + 2);
402+
const paddedExpDigits = expDigits.length < 2 ? "0" + expDigits : expDigits;
403+
const eChar = format === "G" ? "E" : "e";
404+
rep = mantissa + eChar + expSign + paddedExpDigits;
405+
} else {
406+
rep = trimEnd(trimEnd(rep, "0"), ".");
407+
}
397408
break;
409+
}
398410
case "n": case "N":
399411
precision = precision != null ? precision : 2;
400412
rep = toFixed(rep, precision);

tests/Js/Main/StringTests.fs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,21 @@ let tests = testList "Strings" [
511511
String.Format(CultureInfo.InvariantCulture, "{0:P2}", 0.1234) |> equal "12.34 %"
512512
String.Format(CultureInfo.InvariantCulture, "{0:C2}", 1000) |> equal "¤1,000.00"
513513
514+
testCase "G format specifier trims trailing zeros without corrupting exponential notation" <| fun () ->
515+
(123.45).ToString("G17", CultureInfo.InvariantCulture) |> equal "123.45"
516+
(10000.0).ToString("G17", CultureInfo.InvariantCulture) |> equal "10000"
517+
(0.000222).ToString("G17", CultureInfo.InvariantCulture) |> equal "0.000222"
518+
(1e-10).ToString("G17", CultureInfo.InvariantCulture) |> equal "1E-10"
519+
(1e-10).ToString("g17", CultureInfo.InvariantCulture) |> equal "1e-10"
520+
(1.5e10).ToString("G17", CultureInfo.InvariantCulture) |> equal "15000000000"
521+
522+
testCase "G format specifier pads exponent to at least 2 digits" <| fun () ->
523+
(1e-7).ToString("G2", CultureInfo.InvariantCulture) |> equal "1E-07"
524+
(1e7).ToString("G2", CultureInfo.InvariantCulture) |> equal "1E+07"
525+
(1.5e-7).ToString("G2", CultureInfo.InvariantCulture) |> equal "1.5E-07"
526+
(-1e-7).ToString("G2", CultureInfo.InvariantCulture) |> equal "-1E-07"
527+
(1e-7).ToString("g2", CultureInfo.InvariantCulture) |> equal "1e-07"
528+
514529
testCase "x and X format specifiers work with no defined precision" <| fun () ->
515530
String.Format(CultureInfo.InvariantCulture, "{0:X}", 0) |> equal "0"
516531
String.Format(CultureInfo.InvariantCulture, "{0:X}", 0u) |> equal "0"

0 commit comments

Comments
 (0)