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 &nbsp;
+
+// 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)
 			}
 		})
 	}