internal/number: scientific encoding
Implement spec’s suggestion for rendering
scientific notation.
Added TODO for supporting HTML variant.
Change-Id: I05b263f598cec44ad1670c8d74196c3a8745a211
Reviewed-on: https://go-review.googlesource.com/46251
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Nigel Tao <nigeltao@golang.org>
diff --git a/internal/number/format.go b/internal/number/format.go
index a5f2efd..6cb243c 100755
--- a/internal/number/format.go
+++ b/internal/number/format.go
@@ -12,9 +12,17 @@
)
// TODO:
-// - split out the logic that computes the visible digits from the rest of the
-// formatting code (needed for plural).
// - grouping of fractions
+// - allow user-defined superscript notation (such as <sup>4</sup>)
+// - same for non-breaking spaces, like
+
+// Formatting proceeds along the following lines:
+// 0) Compose rounding information from format and context.
+// 1) Convert a number into a Decimal.
+// 2) Sanitize Decimal by adding trailing zeros, removing leading digits, and
+// (non-increment) rounding. The Decimal that results from this is suitable
+// for determining the plural form.
+// 3) Render the Decimal in the localized form.
// Formatter contains all the information needed to render a number.
type Formatter struct {
@@ -325,25 +333,71 @@
}
// exp
- dst = append(dst, f.Symbol(SymExponential)...)
- switch {
- case exp < 0:
- dst = append(dst, f.Symbol(SymMinusSign)...)
- exp = -exp
- case f.Flags&AlwaysExpSign != 0:
- dst = append(dst, f.Symbol(SymPlusSign)...)
- }
buf := [12]byte{}
- b = strconv.AppendUint(buf[:0], uint64(exp), 10)
- for i := len(b); i < int(f.MinExponentDigits); i++ {
+ // TODO: use exponential if superscripting is not available (no Latin
+ // numbers or no tags) and use exponential in all other cases.
+ exponential := f.Symbol(SymExponential)
+ if exponential == "E" {
+ dst = append(dst, "\u202f"...) // NARROW NO-BREAK SPACE
+ dst = append(dst, f.Symbol(SymSuperscriptingExponent)...)
+ dst = append(dst, "\u202f"...) // NARROW NO-BREAK SPACE
+ dst = f.AppendDigit(dst, 1)
dst = f.AppendDigit(dst, 0)
- }
- for _, c := range b {
- dst = f.AppendDigit(dst, c-'0')
+ switch {
+ case exp < 0:
+ dst = append(dst, superMinus...)
+ exp = -exp
+ case f.Flags&AlwaysExpSign != 0:
+ dst = append(dst, superPlus...)
+ }
+ b = strconv.AppendUint(buf[:0], uint64(exp), 10)
+ for i := len(b); i < int(f.MinExponentDigits); i++ {
+ dst = append(dst, superDigits[0]...)
+ }
+ for _, c := range b {
+ dst = append(dst, superDigits[c-'0']...)
+ }
+ } else {
+ dst = append(dst, exponential...)
+ switch {
+ case exp < 0:
+ dst = append(dst, f.Symbol(SymMinusSign)...)
+ exp = -exp
+ case f.Flags&AlwaysExpSign != 0:
+ dst = append(dst, f.Symbol(SymPlusSign)...)
+ }
+ b = strconv.AppendUint(buf[:0], uint64(exp), 10)
+ for i := len(b); i < int(f.MinExponentDigits); i++ {
+ dst = f.AppendDigit(dst, 0)
+ }
+ for _, c := range b {
+ dst = f.AppendDigit(dst, c-'0')
+ }
}
return appendAffix(dst, f, suffix, neg), savedLen, len(dst)
}
+const (
+ superMinus = "\u207B" // SUPERSCRIPT HYPHEN-MINUS
+ superPlus = "\u207A" // SUPERSCRIPT PLUS SIGN
+)
+
+var (
+ // Note: the digits are not sequential!!!
+ superDigits = []string{
+ "\u2070", // SUPERSCRIPT DIGIT ZERO
+ "\u00B9", // SUPERSCRIPT DIGIT ONE
+ "\u00B2", // SUPERSCRIPT DIGIT TWO
+ "\u00B3", // SUPERSCRIPT DIGIT THREE
+ "\u2074", // SUPERSCRIPT DIGIT FOUR
+ "\u2075", // SUPERSCRIPT DIGIT FIVE
+ "\u2076", // SUPERSCRIPT DIGIT SIX
+ "\u2077", // SUPERSCRIPT DIGIT SEVEN
+ "\u2078", // SUPERSCRIPT DIGIT EIGHT
+ "\u2079", // SUPERSCRIPT DIGIT NINE
+ }
+)
+
func (f *Formatter) getAffixes(neg bool) (affix, suffix string) {
str := f.Affix
if str != "" {
diff --git a/internal/number/format_test.go b/internal/number/format_test.go
index 0ec2440..d7b65b0 100755
--- a/internal/number/format_test.go
+++ b/internal/number/format_test.go
@@ -259,60 +259,60 @@
}, {
pattern: "#E0",
test: pairs{
- "0": "0E0",
- "1": "1E0",
- "123.456": "1E2",
+ "0": "0\u202f×\u202f10⁰",
+ "1": "1\u202f×\u202f10⁰",
+ "123.456": "1\u202f×\u202f10²",
},
}, {
pattern: "#E+0",
test: pairs{
- "0": "0E+0",
- "1000": "1E+3",
- "1E100": "1E+100",
- "1E-100": "1E-100",
+ "0": "0\u202f×\u202f10⁺⁰",
+ "1000": "1\u202f×\u202f10⁺³",
+ "1E100": "1\u202f×\u202f10⁺¹⁰⁰",
+ "1E-100": "1\u202f×\u202f10⁻¹⁰⁰",
"NaN": "NaN",
"-Inf": "-∞",
},
}, {
pattern: "##0E00",
test: pairs{
- "100": "100E00",
- "12345": "10E03",
- "123.456": "100E00",
+ "100": "100\u202f×\u202f10⁰⁰",
+ "12345": "10\u202f×\u202f10⁰³",
+ "123.456": "100\u202f×\u202f10⁰⁰",
},
}, {
pattern: "##0.###E00",
test: pairs{
- "100": "100E00",
- "12345": "12.34E03",
- "123.456": "123.4E00",
+ "100": "100\u202f×\u202f10⁰⁰",
+ "12345": "12.34\u202f×\u202f10⁰³",
+ "123.456": "123.4\u202f×\u202f10⁰⁰",
},
}, {
pattern: "##0.000E00",
test: pairs{
- "100": "100.0E00",
- "12345": "12.34E03",
- "123.456": "123.4E00",
+ "100": "100.0\u202f×\u202f10⁰⁰",
+ "12345": "12.34\u202f×\u202f10⁰³",
+ "123.456": "123.4\u202f×\u202f10⁰⁰",
},
}, {
pattern: "@@E0",
test: pairs{
- "0": "0.0E0",
- "99": "9.9E1",
- "0.99": "9.9E-1",
+ "0": "0.0\u202f×\u202f10⁰",
+ "99": "9.9\u202f×\u202f10¹",
+ "0.99": "9.9\u202f×\u202f10⁻¹",
},
}, {
pattern: "@###E00",
test: pairs{
- "0": "0E00",
- "1": "1E00",
- "11": "1.1E01",
- "111": "1.11E02",
- "1111": "1.111E03",
- "11111": "1.111E04",
- "0.1": "1E-01",
- "0.11": "1.1E-01",
- "0.001": "1E-03",
+ "0": "0\u202f×\u202f10⁰⁰",
+ "1": "1\u202f×\u202f10⁰⁰",
+ "11": "1.1\u202f×\u202f10⁰¹",
+ "111": "1.11\u202f×\u202f10⁰²",
+ "1111": "1.111\u202f×\u202f10⁰³",
+ "11111": "1.111\u202f×\u202f10⁰⁴",
+ "0.1": "1\u202f×\u202f10⁻⁰¹",
+ "0.11": "1.1\u202f×\u202f10⁻⁰¹",
+ "0.001": "1\u202f×\u202f10⁻⁰³",
},
}, {
pattern: "*x###",
@@ -339,18 +339,18 @@
"1234.567": "1234.567",
},
}, {
- pattern: "**0.0##E00",
+ pattern: "**0.0#######E00",
test: pairs{
- "0": "**0.0E00",
- "10": "**1.0E01",
- "11": "**1.1E01",
- "111": "*1.11E02",
- "1111": "1.111E03",
- "11111": "1.111E04",
- "11110": "1.111E04",
- "11100": "*1.11E04",
- "11000": "**1.1E04",
- "10000": "**1.0E04",
+ "0": "***0.0\u202f×\u202f10⁰⁰",
+ "10": "***1.0\u202f×\u202f10⁰¹",
+ "11": "***1.1\u202f×\u202f10⁰¹",
+ "111": "**1.11\u202f×\u202f10⁰²",
+ "1111": "*1.111\u202f×\u202f10⁰³",
+ "11111": "1.1111\u202f×\u202f10⁰⁴",
+ "11110": "*1.111\u202f×\u202f10⁰⁴",
+ "11100": "**1.11\u202f×\u202f10⁰⁴",
+ "11000": "***1.1\u202f×\u202f10⁰⁴",
+ "10000": "***1.0\u202f×\u202f10⁰⁴",
},
}, {
pattern: "*xpre#suf",
@@ -443,7 +443,7 @@
dec := mkdec(dec)
buf = f.Format(buf[:0], &dec)
if got := string(buf); got != want {
- t.Errorf("\n got %q\nwant %q", got, want)
+ t.Errorf("\n got %[1]q (%[1]s)\nwant %[2]q (%[2]s)", got, want)
}
})
}
@@ -469,7 +469,7 @@
d := mkdec(tc.num)
b := f.Format(nil, &d)
if got := string(b); got != tc.want {
- t.Errorf("got %q; want %q", got, tc.want)
+ t.Errorf("got %[1]q (%[1]s); want %[2]q (%[2]s)", got, tc.want)
}
})
}
@@ -483,8 +483,8 @@
want string
}{
{f.InitDecimal, "123456.78", "123,456.78"},
- {f.InitScientific, "123456.78", "1.23E5"},
- {f.InitEngineering, "123456.78", "123E3"},
+ {f.InitScientific, "123456.78", "1.23\u202f×\u202f10⁵"},
+ {f.InitEngineering, "123456.78", "123\u202f×\u202f10³"},
{f.InitPercent, "0.1234", "12.34%"},
{f.InitPerMille, "0.1234", "123.40‰"},
@@ -497,7 +497,7 @@
d := mkdec(tc.num)
b := f.Format(nil, &d)
if got := string(b); got != tc.want {
- t.Errorf("got %q; want %q", got, tc.want)
+ t.Errorf("got %[1]q (%[1]s); want %[2]q (%[2]s)", got, tc.want)
}
})
}