internal/number: clean up RoundingContext

- remove Scale and Precision fields
- pass RoundingContext as value (safer)
- fixes a bug as a side-effect

Change-Id: I248eccae2ad7fa5fe411b0893eaf784e8b10aca7
Reviewed-on: https://go-review.googlesource.com/58570
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/decimal.go b/internal/number/decimal.go
index 7c28dc1..443c69e 100644
--- a/internal/number/decimal.go
+++ b/internal/number/decimal.go
@@ -292,7 +292,7 @@
 // A Converter converts a number into decimals according to the given rounding
 // criteria.
 type Converter interface {
-	Convert(d *Decimal, r *RoundingContext)
+	Convert(d *Decimal, r RoundingContext)
 }
 
 const (
@@ -302,7 +302,7 @@
 
 // Convert converts the given number to the decimal representation using the
 // supplied RoundingContext.
-func (d *Decimal) Convert(r *RoundingContext, number interface{}) {
+func (d *Decimal) Convert(r RoundingContext, number interface{}) {
 	switch f := number.(type) {
 	case Converter:
 		d.clear()
@@ -344,7 +344,7 @@
 }
 
 // ConvertInt converts an integer to decimals.
-func (d *Decimal) ConvertInt(r *RoundingContext, signed bool, x uint64) {
+func (d *Decimal) ConvertInt(r RoundingContext, signed bool, x uint64) {
 	if r.Increment > 0 {
 		// TODO: if uint64 is too large, fall back to float64
 		if signed {
@@ -364,7 +364,7 @@
 }
 
 // ConvertFloat converts a floating point number to decimals.
-func (d *Decimal) ConvertFloat(r *RoundingContext, x float64, size int) {
+func (d *Decimal) ConvertFloat(r RoundingContext, x float64, size int) {
 	d.clear()
 	if math.IsNaN(x) {
 		d.NaN = true
@@ -380,11 +380,13 @@
 		return
 	}
 	// Simple case: decimal notation
-	if r.Scale > 0 || r.Increment > 0 || r.Precision == 0 {
-		if int(r.Scale) > len(scales) {
-			x *= math.Pow(10, float64(r.Scale))
+	scale := r.scale()
+	prec := r.precision()
+	if scale > 0 || r.Increment > 0 || prec == 0 {
+		if scale > len(scales) {
+			x *= math.Pow(10, float64(scale))
 		} else {
-			x *= scales[r.Scale]
+			x *= scales[scale]
 		}
 		if r.Increment > 0 {
 			inc := float64(r.Increment)
@@ -395,7 +397,7 @@
 			x = r.Mode.roundFloat(x)
 		}
 		d.fillIntDigits(uint64(math.Abs(x)))
-		d.Exp = int32(len(d.Digits)) - r.Scale
+		d.Exp = int32(len(d.Digits) - scale)
 		return
 	}
 
@@ -407,7 +409,6 @@
 	//   AppendDigits(dst []byte, x float64, base, size, prec int) (digits []byte, exp, accuracy int)
 	// TODO: This only supports the nearest even rounding mode.
 
-	prec := int(r.Precision)
 	if prec > 0 {
 		prec--
 	}
diff --git a/internal/number/decimal_test.go b/internal/number/decimal_test.go
index 11d7644..7860459 100644
--- a/internal/number/decimal_test.go
+++ b/internal/number/decimal_test.go
@@ -23,13 +23,13 @@
 // value NaN, Inf, or -Inf.
 func mkdec(num string) (d Decimal) {
 	var r RoundingContext
-	d.Convert(&r, dec(num))
+	d.Convert(r, dec(num))
 	return
 }
 
 type dec string
 
-func (s dec) Convert(d *Decimal, _ *RoundingContext) {
+func (s dec) Convert(d *Decimal, _ RoundingContext) {
 	num := string(s)
 	if num[0] == '-' {
 		d.Neg = true
@@ -241,14 +241,18 @@
 }
 
 func TestConvert(t *testing.T) {
-	scale2 := &RoundingContext{Scale: 2}
-	scale2away := &RoundingContext{Scale: 2, Mode: AwayFromZero}
-	inc0_05 := &RoundingContext{Increment: 5, Scale: 2}
-	inc50 := &RoundingContext{Increment: 50}
-	prec3 := &RoundingContext{Precision: 3}
+	scale2 := RoundingContext{}
+	scale2.SetScale(2)
+	scale2away := RoundingContext{Mode: AwayFromZero}
+	scale2away.SetScale(2)
+	inc0_05 := RoundingContext{Increment: 5}
+	inc0_05.SetScale(2)
+	inc50 := RoundingContext{Increment: 50}
+	prec3 := RoundingContext{}
+	prec3.SetPrecision(3)
 	testCases := []struct {
 		x   interface{}
-		rc  *RoundingContext
+		rc  RoundingContext
 		out string
 	}{
 		{int8(-34), scale2, "-34"},
@@ -294,7 +298,7 @@
 
 type converter int
 
-func (c converter) Convert(d *Decimal, r *RoundingContext) {
+func (c converter) Convert(d *Decimal, r RoundingContext) {
 	d.Digits = append(d.Digits, 1, 0, 0)
 	d.Exp = 3
 }
diff --git a/internal/number/format.go b/internal/number/format.go
index 9c764dc..744120a 100755
--- a/internal/number/format.go
+++ b/internal/number/format.go
@@ -81,12 +81,12 @@
 
 func (f *Formatter) Append(dst []byte, x interface{}) []byte {
 	var d Decimal
-	r := &f.RoundingContext
+	r := f.RoundingContext
 	d.Convert(r, x)
 	return f.Render(dst, FormatDigits(&d, r))
 }
 
-func FormatDigits(d *Decimal, r *RoundingContext) Digits {
+func FormatDigits(d *Decimal, r RoundingContext) Digits {
 	if r.isScientific() {
 		return scientificVisibleDigits(r, d)
 	}
@@ -94,7 +94,7 @@
 }
 
 func (f *Formatter) Format(dst []byte, d *Decimal) []byte {
-	return f.Render(dst, FormatDigits(d, &f.RoundingContext))
+	return f.Render(dst, FormatDigits(d, f.RoundingContext))
 }
 
 func (f *Formatter) Render(dst []byte, d Digits) []byte {
@@ -142,7 +142,7 @@
 	return result
 }
 
-func decimalVisibleDigits(r *RoundingContext, d *Decimal) Digits {
+func decimalVisibleDigits(r RoundingContext, d *Decimal) Digits {
 	if d.NaN || d.Inf {
 		return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}}
 	}
@@ -282,7 +282,7 @@
 	return appendAffix(dst, f, suffix, neg), savedLen, len(dst)
 }
 
-func scientificVisibleDigits(r *RoundingContext, d *Decimal) Digits {
+func scientificVisibleDigits(r RoundingContext, d *Decimal) Digits {
 	if d.NaN || d.Inf {
 		return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}}
 	}
diff --git a/internal/number/format_test.go b/internal/number/format_test.go
index c56cbc1..fc7c36d 100755
--- a/internal/number/format_test.go
+++ b/internal/number/format_test.go
@@ -453,7 +453,7 @@
 			buf := make([]byte, 100)
 			t.Run(tc.pattern+"/"+num, func(t *testing.T) {
 				var d Decimal
-				d.Convert(&f.RoundingContext, dec(num))
+				d.Convert(f.RoundingContext, dec(num))
 				buf = f.Format(buf[:0], &d)
 				if got := string(buf); got != want {
 					t.Errorf("\n got %[1]q (%[1]s)\nwant %[2]q (%[2]s)", got, want)
@@ -480,7 +480,7 @@
 			var f Formatter
 			f.InitDecimal(tc.tag)
 			var d Decimal
-			d.Convert(&f.RoundingContext, dec(tc.num))
+			d.Convert(f.RoundingContext, dec(tc.num))
 			b := f.Format(nil, &d)
 			if got := string(b); got != tc.want {
 				t.Errorf("got %[1]q (%[1]s); want %[2]q (%[2]s)", got, tc.want)
@@ -508,7 +508,7 @@
 			tc.init(language.English)
 			f.SetScale(2)
 			var d Decimal
-			d.Convert(&f.RoundingContext, dec(tc.num))
+			d.Convert(f.RoundingContext, dec(tc.num))
 			b := f.Format(nil, &d)
 			if got := string(b); got != tc.want {
 				t.Errorf("got %[1]q (%[1]s); want %[2]q (%[2]s)", got, tc.want)
diff --git a/internal/number/pattern.go b/internal/number/pattern.go
index 881308e..89c2a1c 100644
--- a/internal/number/pattern.go
+++ b/internal/number/pattern.go
@@ -39,19 +39,14 @@
 //
 // This type is only intended for internal use.
 type Pattern struct {
-	// TODO: this struct can be packed a lot better than it is now. Should be
-	// possible to make it 32 bytes.
-
-	Affix     string // includes prefix and suffix. First byte is prefix length.
-	Offset    uint16 // Offset into Affix for prefix and suffix
-	NegOffset uint16 // Offset into Affix for negative prefix and suffix or 0.
-
-	FormatWidth uint16
-
-	PadRune rune
-
 	RoundingContext
 
+	Affix       string // includes prefix and suffix. First byte is prefix length.
+	Offset      uint16 // Offset into Affix for prefix and suffix
+	NegOffset   uint16 // Offset into Affix for negative prefix and suffix or 0.
+	PadRune     rune
+	FormatWidth uint16
+
 	GroupingSize [2]uint8
 	Flags        PatternFlag
 }
@@ -60,45 +55,44 @@
 // It contains all information needed to determine the "visible digits" as
 // required by the pluralization rules.
 type RoundingContext struct {
-	Precision int32 // maximum number of significant digits.
-	Scale     int32 // maximum number of decimals after the dot.
-
-	// if > 0, round to Increment * 10^-Scale
-	Increment uint32 // Use Min*Digits to determine scale
+	Increment uint32 // if > 0, round to Increment * 10^-scale()
+	// TODO: unify these two fields so that there is a more unambiguous meaning
+	// of how precision is handled.
+	MaxSignificantDigits int16 // -1 is infinite precision
+	MaxFractionDigits    uint16
 
 	Mode RoundingMode
 
 	DigitShift uint8 // Number of decimals to shift. Used for % and ‰.
 
 	// Number of digits.
-	// TODO: consider using uint32
-	MinIntegerDigits     uint8
+	MinIntegerDigits uint8
+
 	MaxIntegerDigits     uint8
 	MinFractionDigits    uint8
-	MaxFractionDigits    uint8
 	MinSignificantDigits uint8
-	MaxSignificantDigits uint8
 
 	MinExponentDigits uint8
 }
 
-func (r *RoundingContext) update() {
-	if r.Scale > 0 {
-		r.SetScale(int(r.Scale))
+func (r *RoundingContext) scale() int {
+	// scale is 0 when precision is set.
+	if r.MaxSignificantDigits != 0 {
+		return 0
 	}
-	if r.Precision > 0 {
-		r.SetPrecision(int(r.Precision))
-	}
+	return int(r.MaxFractionDigits)
 }
 
+func (r *RoundingContext) precision() int { return int(r.MaxSignificantDigits) }
+
 // SetScale fixes the RoundingContext to a fixed number of fraction digits.
 func (r *RoundingContext) SetScale(scale int) {
 	r.MinFractionDigits = uint8(scale)
-	r.MaxFractionDigits = uint8(scale)
+	r.MaxFractionDigits = uint16(scale)
 }
 
 func (r *RoundingContext) SetPrecision(prec int) {
-	r.MaxSignificantDigits = uint8(prec)
+	r.MaxSignificantDigits = int16(prec)
 }
 
 func (r *RoundingContext) isScientific() bool {
@@ -420,7 +414,7 @@
 func (p *parser) normalizeSigDigitsWithExponent() state {
 	p.MinIntegerDigits, p.MaxIntegerDigits = 1, 1
 	p.MinFractionDigits = p.MinSignificantDigits - 1
-	p.MaxFractionDigits = p.MaxSignificantDigits - 1
+	p.MaxFractionDigits = uint16(p.MaxSignificantDigits) - 1
 	p.MinSignificantDigits, p.MaxSignificantDigits = 0, 0
 	return p.exponent
 }
diff --git a/internal/number/tables.go b/internal/number/tables.go
index b140234..2cf16f5 100644
--- a/internal/number/tables.go
+++ b/internal/number/tables.go
@@ -846,323 +846,291 @@
 	0x04, 0x04,
 } // Size: 778 bytes
 
-var formats = []Pattern{Pattern{Affix: "",
+var formats = []Pattern{Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+	MaxSignificantDigits: 0,
+	MaxFractionDigits:    0x0,
+	Mode:                 0x0,
+	DigitShift:           0x0,
+	MinIntegerDigits:     0x0,
+	MaxIntegerDigits:     0x0,
+	MinFractionDigits:    0x0,
+	MinSignificantDigits: 0x0,
+	MinExponentDigits:    0x0},
+	Affix:       "",
 	Offset:      0x0,
 	NegOffset:   0x0,
-	FormatWidth: 0x0,
 	PadRune:     0,
-	RoundingContext: RoundingContext{Mode: 0x0,
-		Precision:            0,
-		Scale:                0,
-		Increment:            0x0,
+	FormatWidth: 0x0,
+	GroupingSize: [2]uint8{0x0,
+		0x0},
+	Flags: 0x0},
+	Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+		MaxSignificantDigits: 0,
+		MaxFractionDigits:    0x3,
+		Mode:                 0x0,
+		DigitShift:           0x0,
+		MinIntegerDigits:     0x1,
+		MaxIntegerDigits:     0x0,
+		MinFractionDigits:    0x0,
+		MinSignificantDigits: 0x0,
+		MinExponentDigits:    0x0},
+		Affix:       "",
+		Offset:      0x0,
+		NegOffset:   0x0,
+		PadRune:     0,
+		FormatWidth: 0x9,
+		GroupingSize: [2]uint8{0x3,
+			0x0},
+		Flags: 0x0},
+	Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+		MaxSignificantDigits: 0,
+		MaxFractionDigits:    0x0,
+		Mode:                 0x0,
+		DigitShift:           0x0,
+		MinIntegerDigits:     0x0,
+		MaxIntegerDigits:     0x1,
+		MinFractionDigits:    0x0,
+		MinSignificantDigits: 0x0,
+		MinExponentDigits:    0x1},
+		Affix:       "",
+		Offset:      0x0,
+		NegOffset:   0x0,
+		PadRune:     0,
+		FormatWidth: 0x3,
+		GroupingSize: [2]uint8{0x0,
+			0x0},
+		Flags: 0x0},
+	Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+		MaxSignificantDigits: 0,
+		MaxFractionDigits:    0x0,
+		Mode:                 0x0,
+		DigitShift:           0x2,
+		MinIntegerDigits:     0x1,
+		MaxIntegerDigits:     0x0,
+		MinFractionDigits:    0x0,
+		MinSignificantDigits: 0x0,
+		MinExponentDigits:    0x0},
+		Affix:       "\x00\x03\u00a0%",
+		Offset:      0x0,
+		NegOffset:   0x0,
+		PadRune:     0,
+		FormatWidth: 0x7,
+		GroupingSize: [2]uint8{0x3,
+			0x0},
+		Flags: 0x0},
+	Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+		MaxSignificantDigits: 0,
+		MaxFractionDigits:    0x0,
+		Mode:                 0x0,
+		DigitShift:           0x2,
+		MinIntegerDigits:     0x1,
+		MaxIntegerDigits:     0x0,
+		MinFractionDigits:    0x0,
+		MinSignificantDigits: 0x0,
+		MinExponentDigits:    0x0},
+		Affix:       "\x00\x01%",
+		Offset:      0x0,
+		NegOffset:   0x0,
+		PadRune:     0,
+		FormatWidth: 0x6,
+		GroupingSize: [2]uint8{0x3,
+			0x0},
+		Flags: 0x0},
+	Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+		MaxSignificantDigits: 0,
+		MaxFractionDigits:    0x3,
+		Mode:                 0x0,
+		DigitShift:           0x0,
+		MinIntegerDigits:     0x1,
+		MaxIntegerDigits:     0x0,
+		MinFractionDigits:    0x0,
+		MinSignificantDigits: 0x0,
+		MinExponentDigits:    0x0},
+		Affix:       "",
+		Offset:      0x0,
+		NegOffset:   0x0,
+		PadRune:     0,
+		FormatWidth: 0xc,
+		GroupingSize: [2]uint8{0x3,
+			0x2},
+		Flags: 0x0},
+	Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+		MaxSignificantDigits: 0,
+		MaxFractionDigits:    0x0,
+		Mode:                 0x0,
+		DigitShift:           0x2,
+		MinIntegerDigits:     0x1,
+		MaxIntegerDigits:     0x0,
+		MinFractionDigits:    0x0,
+		MinSignificantDigits: 0x0,
+		MinExponentDigits:    0x0},
+		Affix:       "\x00\x01%",
+		Offset:      0x0,
+		NegOffset:   0x0,
+		PadRune:     0,
+		FormatWidth: 0x9,
+		GroupingSize: [2]uint8{0x3,
+			0x2},
+		Flags: 0x0},
+	Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+		MaxSignificantDigits: 0,
+		MaxFractionDigits:    0x0,
+		Mode:                 0x0,
+		DigitShift:           0x2,
+		MinIntegerDigits:     0x1,
+		MaxIntegerDigits:     0x0,
+		MinFractionDigits:    0x0,
+		MinSignificantDigits: 0x0,
+		MinExponentDigits:    0x0},
+		Affix:       "\x00\x03\u00a0%",
+		Offset:      0x0,
+		NegOffset:   0x0,
+		PadRune:     0,
+		FormatWidth: 0xa,
+		GroupingSize: [2]uint8{0x3,
+			0x2},
+		Flags: 0x0},
+	Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+		MaxSignificantDigits: 0,
+		MaxFractionDigits:    0x6,
+		Mode:                 0x0,
+		DigitShift:           0x0,
+		MinIntegerDigits:     0x1,
+		MaxIntegerDigits:     0x0,
+		MinFractionDigits:    0x0,
+		MinSignificantDigits: 0x0,
+		MinExponentDigits:    0x0},
+		Affix:       "",
+		Offset:      0x0,
+		NegOffset:   0x0,
+		PadRune:     0,
+		FormatWidth: 0x9,
+		GroupingSize: [2]uint8{0x0,
+			0x0},
+		Flags: 0x0},
+	Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+		MaxSignificantDigits: 0,
+		MaxFractionDigits:    0x6,
+		Mode:                 0x0,
+		DigitShift:           0x0,
+		MinIntegerDigits:     0x1,
+		MaxIntegerDigits:     0x0,
+		MinFractionDigits:    0x6,
+		MinSignificantDigits: 0x0,
+		MinExponentDigits:    0x3},
+		Affix:       "",
+		Offset:      0x0,
+		NegOffset:   0x0,
+		PadRune:     0,
+		FormatWidth: 0xd,
+		GroupingSize: [2]uint8{0x0,
+			0x0},
+		Flags: 0x4},
+	Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+		MaxSignificantDigits: 0,
+		MaxFractionDigits:    0x0,
+		Mode:                 0x0,
+		DigitShift:           0x2,
+		MinIntegerDigits:     0x1,
+		MaxIntegerDigits:     0x0,
+		MinFractionDigits:    0x0,
+		MinSignificantDigits: 0x0,
+		MinExponentDigits:    0x0},
+		Affix:       "\x00\x01%",
+		Offset:      0x0,
+		NegOffset:   0x0,
+		PadRune:     0,
+		FormatWidth: 0x3,
+		GroupingSize: [2]uint8{0x0,
+			0x0},
+		Flags: 0x0},
+	Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+		MaxSignificantDigits: 0,
+		MaxFractionDigits:    0x0,
+		Mode:                 0x0,
+		DigitShift:           0x2,
+		MinIntegerDigits:     0x1,
+		MaxIntegerDigits:     0x0,
+		MinFractionDigits:    0x0,
+		MinSignificantDigits: 0x0,
+		MinExponentDigits:    0x0},
+		Affix:       "\x03%\u00a0\x00",
+		Offset:      0x0,
+		NegOffset:   0x0,
+		PadRune:     0,
+		FormatWidth: 0x7,
+		GroupingSize: [2]uint8{0x3,
+			0x0},
+		Flags: 0x0},
+	Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+		MaxSignificantDigits: 0,
+		MaxFractionDigits:    0x0,
+		Mode:                 0x0,
+		DigitShift:           0x2,
+		MinIntegerDigits:     0x1,
+		MaxIntegerDigits:     0x0,
+		MinFractionDigits:    0x0,
+		MinSignificantDigits: 0x0,
+		MinExponentDigits:    0x0},
+		Affix:       "\x03%\u00a0\x00\x04%\u00a0-\x00",
+		Offset:      0x0,
+		NegOffset:   0x5,
+		PadRune:     0,
+		FormatWidth: 0x7,
+		GroupingSize: [2]uint8{0x3,
+			0x0},
+		Flags: 0x0},
+	Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+		MaxSignificantDigits: 0,
+		MaxFractionDigits:    0x0,
+		Mode:                 0x0,
+		DigitShift:           0x0,
+		MinIntegerDigits:     0x0,
+		MaxIntegerDigits:     0x1,
+		MinFractionDigits:    0x0,
+		MinSignificantDigits: 0x0,
+		MinExponentDigits:    0x1},
+		Affix:       "\x01[\x01]",
+		Offset:      0x0,
+		NegOffset:   0x0,
+		PadRune:     0,
+		FormatWidth: 0x5,
+		GroupingSize: [2]uint8{0x0,
+			0x0},
+		Flags: 0x0},
+	Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+		MaxSignificantDigits: 0,
+		MaxFractionDigits:    0x0,
+		Mode:                 0x0,
 		DigitShift:           0x0,
 		MinIntegerDigits:     0x0,
 		MaxIntegerDigits:     0x0,
 		MinFractionDigits:    0x0,
-		MaxFractionDigits:    0x0,
 		MinSignificantDigits: 0x0,
-		MaxSignificantDigits: 0x0,
 		MinExponentDigits:    0x0},
-	GroupingSize: [2]uint8{0x0,
-		0x0},
-	Flags: 0x0},
-	Pattern{Affix: "",
+		Affix:       "",
 		Offset:      0x0,
 		NegOffset:   0x0,
-		FormatWidth: 0x9,
 		PadRune:     0,
-		RoundingContext: RoundingContext{Mode: 0x0,
-			Precision:            0,
-			Scale:                0,
-			Increment:            0x0,
-			DigitShift:           0x0,
-			MinIntegerDigits:     0x1,
-			MaxIntegerDigits:     0x0,
-			MinFractionDigits:    0x0,
-			MaxFractionDigits:    0x3,
-			MinSignificantDigits: 0x0,
-			MaxSignificantDigits: 0x0,
-			MinExponentDigits:    0x0},
-		GroupingSize: [2]uint8{0x3,
-			0x0},
-		Flags: 0x0},
-	Pattern{Affix: "",
-		Offset:      0x0,
-		NegOffset:   0x0,
-		FormatWidth: 0x3,
-		PadRune:     0,
-		RoundingContext: RoundingContext{Mode: 0x0,
-			Precision:            0,
-			Scale:                0,
-			Increment:            0x0,
-			DigitShift:           0x0,
-			MinIntegerDigits:     0x0,
-			MaxIntegerDigits:     0x1,
-			MinFractionDigits:    0x0,
-			MaxFractionDigits:    0x0,
-			MinSignificantDigits: 0x0,
-			MaxSignificantDigits: 0x0,
-			MinExponentDigits:    0x1},
-		GroupingSize: [2]uint8{0x0,
-			0x0},
-		Flags: 0x0},
-	Pattern{Affix: "\x00\x03\u00a0%",
-		Offset:      0x0,
-		NegOffset:   0x0,
-		FormatWidth: 0x7,
-		PadRune:     0,
-		RoundingContext: RoundingContext{Mode: 0x0,
-			Precision:            0,
-			Scale:                0,
-			Increment:            0x0,
-			DigitShift:           0x2,
-			MinIntegerDigits:     0x1,
-			MaxIntegerDigits:     0x0,
-			MinFractionDigits:    0x0,
-			MaxFractionDigits:    0x0,
-			MinSignificantDigits: 0x0,
-			MaxSignificantDigits: 0x0,
-			MinExponentDigits:    0x0},
-		GroupingSize: [2]uint8{0x3,
-			0x0},
-		Flags: 0x0},
-	Pattern{Affix: "\x00\x01%",
-		Offset:      0x0,
-		NegOffset:   0x0,
-		FormatWidth: 0x6,
-		PadRune:     0,
-		RoundingContext: RoundingContext{Mode: 0x0,
-			Precision:            0,
-			Scale:                0,
-			Increment:            0x0,
-			DigitShift:           0x2,
-			MinIntegerDigits:     0x1,
-			MaxIntegerDigits:     0x0,
-			MinFractionDigits:    0x0,
-			MaxFractionDigits:    0x0,
-			MinSignificantDigits: 0x0,
-			MaxSignificantDigits: 0x0,
-			MinExponentDigits:    0x0},
-		GroupingSize: [2]uint8{0x3,
-			0x0},
-		Flags: 0x0},
-	Pattern{Affix: "",
-		Offset:      0x0,
-		NegOffset:   0x0,
-		FormatWidth: 0xc,
-		PadRune:     0,
-		RoundingContext: RoundingContext{Mode: 0x0,
-			Precision:            0,
-			Scale:                0,
-			Increment:            0x0,
-			DigitShift:           0x0,
-			MinIntegerDigits:     0x1,
-			MaxIntegerDigits:     0x0,
-			MinFractionDigits:    0x0,
-			MaxFractionDigits:    0x3,
-			MinSignificantDigits: 0x0,
-			MaxSignificantDigits: 0x0,
-			MinExponentDigits:    0x0},
-		GroupingSize: [2]uint8{0x3,
-			0x2},
-		Flags: 0x0},
-	Pattern{Affix: "\x00\x01%",
-		Offset:      0x0,
-		NegOffset:   0x0,
-		FormatWidth: 0x9,
-		PadRune:     0,
-		RoundingContext: RoundingContext{Mode: 0x0,
-			Precision:            0,
-			Scale:                0,
-			Increment:            0x0,
-			DigitShift:           0x2,
-			MinIntegerDigits:     0x1,
-			MaxIntegerDigits:     0x0,
-			MinFractionDigits:    0x0,
-			MaxFractionDigits:    0x0,
-			MinSignificantDigits: 0x0,
-			MaxSignificantDigits: 0x0,
-			MinExponentDigits:    0x0},
-		GroupingSize: [2]uint8{0x3,
-			0x2},
-		Flags: 0x0},
-	Pattern{Affix: "\x00\x03\u00a0%",
-		Offset:      0x0,
-		NegOffset:   0x0,
-		FormatWidth: 0xa,
-		PadRune:     0,
-		RoundingContext: RoundingContext{Mode: 0x0,
-			Precision:            0,
-			Scale:                0,
-			Increment:            0x0,
-			DigitShift:           0x2,
-			MinIntegerDigits:     0x1,
-			MaxIntegerDigits:     0x0,
-			MinFractionDigits:    0x0,
-			MaxFractionDigits:    0x0,
-			MinSignificantDigits: 0x0,
-			MaxSignificantDigits: 0x0,
-			MinExponentDigits:    0x0},
-		GroupingSize: [2]uint8{0x3,
-			0x2},
-		Flags: 0x0},
-	Pattern{Affix: "",
-		Offset:      0x0,
-		NegOffset:   0x0,
-		FormatWidth: 0x9,
-		PadRune:     0,
-		RoundingContext: RoundingContext{Mode: 0x0,
-			Precision:            0,
-			Scale:                0,
-			Increment:            0x0,
-			DigitShift:           0x0,
-			MinIntegerDigits:     0x1,
-			MaxIntegerDigits:     0x0,
-			MinFractionDigits:    0x0,
-			MaxFractionDigits:    0x6,
-			MinSignificantDigits: 0x0,
-			MaxSignificantDigits: 0x0,
-			MinExponentDigits:    0x0},
-		GroupingSize: [2]uint8{0x0,
-			0x0},
-		Flags: 0x0},
-	Pattern{Affix: "",
-		Offset:      0x0,
-		NegOffset:   0x0,
-		FormatWidth: 0xd,
-		PadRune:     0,
-		RoundingContext: RoundingContext{Mode: 0x0,
-			Precision:            0,
-			Scale:                0,
-			Increment:            0x0,
-			DigitShift:           0x0,
-			MinIntegerDigits:     0x1,
-			MaxIntegerDigits:     0x0,
-			MinFractionDigits:    0x6,
-			MaxFractionDigits:    0x6,
-			MinSignificantDigits: 0x0,
-			MaxSignificantDigits: 0x0,
-			MinExponentDigits:    0x3},
-		GroupingSize: [2]uint8{0x0,
-			0x0},
-		Flags: 0x4},
-	Pattern{Affix: "\x00\x01%",
-		Offset:      0x0,
-		NegOffset:   0x0,
-		FormatWidth: 0x3,
-		PadRune:     0,
-		RoundingContext: RoundingContext{Mode: 0x0,
-			Precision:            0,
-			Scale:                0,
-			Increment:            0x0,
-			DigitShift:           0x2,
-			MinIntegerDigits:     0x1,
-			MaxIntegerDigits:     0x0,
-			MinFractionDigits:    0x0,
-			MaxFractionDigits:    0x0,
-			MinSignificantDigits: 0x0,
-			MaxSignificantDigits: 0x0,
-			MinExponentDigits:    0x0},
-		GroupingSize: [2]uint8{0x0,
-			0x0},
-		Flags: 0x0},
-	Pattern{Affix: "\x03%\u00a0\x00",
-		Offset:      0x0,
-		NegOffset:   0x0,
-		FormatWidth: 0x7,
-		PadRune:     0,
-		RoundingContext: RoundingContext{Mode: 0x0,
-			Precision:            0,
-			Scale:                0,
-			Increment:            0x0,
-			DigitShift:           0x2,
-			MinIntegerDigits:     0x1,
-			MaxIntegerDigits:     0x0,
-			MinFractionDigits:    0x0,
-			MaxFractionDigits:    0x0,
-			MinSignificantDigits: 0x0,
-			MaxSignificantDigits: 0x0,
-			MinExponentDigits:    0x0},
-		GroupingSize: [2]uint8{0x3,
-			0x0},
-		Flags: 0x0},
-	Pattern{Affix: "\x03%\u00a0\x00\x04%\u00a0-\x00",
-		Offset:      0x0,
-		NegOffset:   0x5,
-		FormatWidth: 0x7,
-		PadRune:     0,
-		RoundingContext: RoundingContext{Mode: 0x0,
-			Precision:            0,
-			Scale:                0,
-			Increment:            0x0,
-			DigitShift:           0x2,
-			MinIntegerDigits:     0x1,
-			MaxIntegerDigits:     0x0,
-			MinFractionDigits:    0x0,
-			MaxFractionDigits:    0x0,
-			MinSignificantDigits: 0x0,
-			MaxSignificantDigits: 0x0,
-			MinExponentDigits:    0x0},
-		GroupingSize: [2]uint8{0x3,
-			0x0},
-		Flags: 0x0},
-	Pattern{Affix: "\x01[\x01]",
-		Offset:      0x0,
-		NegOffset:   0x0,
-		FormatWidth: 0x5,
-		PadRune:     0,
-		RoundingContext: RoundingContext{Mode: 0x0,
-			Precision:            0,
-			Scale:                0,
-			Increment:            0x0,
-			DigitShift:           0x0,
-			MinIntegerDigits:     0x0,
-			MaxIntegerDigits:     0x1,
-			MinFractionDigits:    0x0,
-			MaxFractionDigits:    0x0,
-			MinSignificantDigits: 0x0,
-			MaxSignificantDigits: 0x0,
-			MinExponentDigits:    0x1},
-		GroupingSize: [2]uint8{0x0,
-			0x0},
-		Flags: 0x0},
-	Pattern{Affix: "",
-		Offset:      0x0,
-		NegOffset:   0x0,
 		FormatWidth: 0x1,
-		PadRune:     0,
-		RoundingContext: RoundingContext{Mode: 0x0,
-			Precision:            0,
-			Scale:                0,
-			Increment:            0x0,
-			DigitShift:           0x0,
-			MinIntegerDigits:     0x0,
-			MaxIntegerDigits:     0x0,
-			MinFractionDigits:    0x0,
-			MaxFractionDigits:    0x0,
-			MinSignificantDigits: 0x0,
-			MaxSignificantDigits: 0x0,
-			MinExponentDigits:    0x0},
 		GroupingSize: [2]uint8{0x0,
 			0x0},
 		Flags: 0x0},
-	Pattern{Affix: "\x01%\x00",
+	Pattern{RoundingContext: RoundingContext{Increment: 0x0,
+		MaxSignificantDigits: 0,
+		MaxFractionDigits:    0x0,
+		Mode:                 0x0,
+		DigitShift:           0x2,
+		MinIntegerDigits:     0x1,
+		MaxIntegerDigits:     0x0,
+		MinFractionDigits:    0x0,
+		MinSignificantDigits: 0x0,
+		MinExponentDigits:    0x0},
+		Affix:       "\x01%\x00",
 		Offset:      0x0,
 		NegOffset:   0x0,
-		FormatWidth: 0x6,
 		PadRune:     0,
-		RoundingContext: RoundingContext{Mode: 0x0,
-			Precision:            0,
-			Scale:                0,
-			Increment:            0x0,
-			DigitShift:           0x2,
-			MinIntegerDigits:     0x1,
-			MaxIntegerDigits:     0x0,
-			MinFractionDigits:    0x0,
-			MaxFractionDigits:    0x0,
-			MinSignificantDigits: 0x0,
-			MaxSignificantDigits: 0x0,
-			MinExponentDigits:    0x0},
+		FormatWidth: 0x6,
 		GroupingSize: [2]uint8{0x3,
 			0x0},
 		Flags: 0x0}}
diff --git a/message/fmt_test.go b/message/fmt_test.go
index 0dbedcb..b22420d 100755
--- a/message/fmt_test.go
+++ b/message/fmt_test.go
@@ -565,8 +565,8 @@
 
 	// old test/fmt_test.go
 	{"%e", 1.0, "1.000000\u202f×\u202f10⁰⁰"},
-	{"%e", 1234.5678e3, "1.234570\u202f×\u202f10⁰⁶"},
-	{"%e", 1234.5678e-8, "1.234570\u202f×\u202f10⁻⁰⁵"},
+	{"%e", 1234.5678e3, "1.234568\u202f×\u202f10⁰⁶"},
+	{"%e", 1234.5678e-8, "1.234568\u202f×\u202f10⁻⁰⁵"},
 	{"%e", -7.0, "-7.000000\u202f×\u202f10⁰⁰"},
 	{"%e", -1e-9, "-1.000000\u202f×\u202f10⁻⁰⁹"},
 	{"%f", 1234.5678e3, "1,234,567.800000"},
@@ -580,8 +580,8 @@
 	{"%g", -1e-9, "-1\u202f×\u202f10⁻⁰⁹"},
 	{"%g", float32(-1e-9), "-1\u202f×\u202f10⁻⁰⁹"},
 	{"%E", 1.0, "1.000000\u202f×\u202f10⁰⁰"},
-	{"%E", 1234.5678e3, "1.234570\u202f×\u202f10⁰⁶"},
-	{"%E", 1234.5678e-8, "1.234570\u202f×\u202f10⁻⁰⁵"},
+	{"%E", 1234.5678e3, "1.234568\u202f×\u202f10⁰⁶"},
+	{"%E", 1234.5678e-8, "1.234568\u202f×\u202f10⁻⁰⁵"},
 	{"%E", -7.0, "-7.000000\u202f×\u202f10⁰⁰"},
 	{"%E", -1e-9, "-1.000000\u202f×\u202f10⁻⁰⁹"},
 	{"%G", 1234.5678e3, "1.2345678\u202f×\u202f10⁰⁶"},
diff --git a/message/print.go b/message/print.go
index 8c8a23f..c0bef3f 100644
--- a/message/print.go
+++ b/message/print.go
@@ -237,7 +237,7 @@
 		if p.fmt.sharp || p.fmt.sharpV {
 			p.fmt.fmt_float(v, size, verb, -1)
 		} else {
-			p.fmtVariableFloat(v, size, -1)
+			p.fmtVariableFloat(v, size)
 		}
 	case 'e', 'E':
 		if p.fmt.sharp || p.fmt.sharpV {
@@ -284,7 +284,7 @@
 	f.MinIntegerDigits = 1
 	f.MaxIntegerDigits = 0
 	f.MinFractionDigits = uint8(minFrac)
-	f.MaxFractionDigits = uint8(maxFrac)
+	f.MaxFractionDigits = uint16(maxFrac)
 	p.setFlags(f)
 	f.PadRune = 0
 	if p.fmt.widPresent {
@@ -308,8 +308,13 @@
 
 func (p *printer) initScientific(minFrac, maxFrac int) {
 	f := &p.toScientific
-	f.MinFractionDigits = uint8(minFrac)
-	f.MaxFractionDigits = uint8(maxFrac)
+	if maxFrac < 0 {
+		f.SetPrecision(maxFrac)
+	} else {
+		f.SetPrecision(maxFrac + 1)
+		f.MinFractionDigits = uint8(minFrac)
+		f.MaxFractionDigits = uint16(maxFrac)
+	}
 	f.MinExponentDigits = 2
 	p.setFlags(f)
 	f.PadRune = 0
@@ -328,8 +333,6 @@
 
 func (p *printer) fmtDecimalInt(v uint64, isSigned bool) {
 	var d number.Decimal
-	p.toDecimal.RoundingContext.Scale = 0
-	d.ConvertInt(&p.toDecimal.RoundingContext, isSigned, v)
 
 	f := &p.toDecimal
 	if p.fmt.precPresent {
@@ -344,6 +347,7 @@
 	} else {
 		p.initDecimal(0, 0)
 	}
+	d.ConvertInt(p.toDecimal.RoundingContext, isSigned, v)
 
 	out := p.toDecimal.Format([]byte(nil), &d)
 	p.Buffer.Write(out)
@@ -354,22 +358,21 @@
 	if p.fmt.precPresent {
 		prec = p.fmt.prec
 	}
-	p.toDecimal.RoundingContext.Scale = int32(prec)
-	d.ConvertFloat(&p.toDecimal.RoundingContext, v, size)
-
 	p.initDecimal(prec, prec)
+	d.ConvertFloat(p.toDecimal.RoundingContext, v, size)
 
 	out := p.toDecimal.Format([]byte(nil), &d)
 	p.Buffer.Write(out)
 }
 
-func (p *printer) fmtVariableFloat(v float64, size, prec int) {
+func (p *printer) fmtVariableFloat(v float64, size int) {
+	prec := -1
 	if p.fmt.precPresent {
 		prec = p.fmt.prec
 	}
 	var d number.Decimal
-	p.toScientific.RoundingContext.Precision = int32(prec)
-	d.ConvertFloat(&p.toScientific.RoundingContext, v, size)
+	p.initScientific(0, prec)
+	d.ConvertFloat(p.toScientific.RoundingContext, v, size)
 
 	// Copy logic of 'g' formatting from strconv. It is simplified a bit as
 	// we don't have to mind having prec > len(d.Digits).
@@ -407,10 +410,9 @@
 	if p.fmt.precPresent {
 		prec = p.fmt.prec
 	}
-	p.toScientific.RoundingContext.Precision = int32(prec)
-	d.ConvertFloat(&p.toScientific.RoundingContext, v, size)
-
 	p.initScientific(prec, prec)
+	rc := p.toScientific.RoundingContext
+	d.ConvertFloat(rc, v, size)
 
 	out := p.toScientific.Format([]byte(nil), &d)
 	p.Buffer.Write(out)