Skip to content

Commit 867ed12

Browse files
fix the rounding bug in roundShortest method (shopspring#161)
* fix: fix rounding in FormatFloat fallback path
1 parent 6fe01c1 commit 867ed12

File tree

2 files changed

+55
-11
lines changed

2 files changed

+55
-11
lines changed

decimal_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ var testTable = []*testEnt{
5757
{2e250, "20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "", ""},
5858
{math.MaxInt64, strconv.FormatFloat(float64(math.MaxInt64), 'f', -1, 64), "", strconv.FormatInt(math.MaxInt64, 10)},
5959
{1.29067116156722e-309, "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000129067116156722", "", "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001290671161567218558822290567835270536800098852722416870074139002112543896676308448335063375297788379444685193974290737962187240854947838776604607190387984577130572928111657710645015086812756013489109884753559084166516937690932698276436869274093950997935137476803610007959500457935217950764794724766740819156974617155861568214427828145972181876775307023388139991104942469299524961281641158436752347582767153796914843896176260096039358494077706152272661453132497761307744086665088096215425146090058519888494342944692629602847826300550628670375451325582843627504604013541465361435761965354140678551369499812124085312128659002910905639984075064968459581691226705666561364681985266583563078466180095375402399087817404368974165082030458595596655868575908243656158447265625000000000000000000000000000000000000004440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},
60+
// go Issue 29491.
61+
{498484681984085570, "498484681984085570", "", ""},
62+
{5.8339553793802237e+23, "583395537938022370000000", "", ""},
6063
}
6164

6265
var testTableScientificNotation = map[string]string{

rounding.go

+52-11
Original file line numberDiff line numberDiff line change
@@ -80,39 +80,80 @@ func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
8080
// would round to the original mantissa and not the neighbors.
8181
inclusive := mant%2 == 0
8282

83+
// As we walk the digits we want to know whether rounding up would fall
84+
// within the upper bound. This is tracked by upperdelta:
85+
//
86+
// If upperdelta == 0, the digits of d and upper are the same so far.
87+
//
88+
// If upperdelta == 1, we saw a difference of 1 between d and upper on a
89+
// previous digit and subsequently only 9s for d and 0s for upper.
90+
// (Thus rounding up may fall outside the bound, if it is exclusive.)
91+
//
92+
// If upperdelta == 2, then the difference is greater than 1
93+
// and we know that rounding up falls within the bound.
94+
var upperdelta uint8
95+
8396
// Now we can figure out the minimum number of digits required.
8497
// Walk along until d has distinguished itself from upper and lower.
85-
for i := 0; i < d.nd; i++ {
98+
for ui := 0; ; ui++ {
99+
// lower, d, and upper may have the decimal points at different
100+
// places. In this case upper is the longest, so we iterate from
101+
// ui==0 and start li and mi at (possibly) -1.
102+
mi := ui - upper.dp + d.dp
103+
if mi >= d.nd {
104+
break
105+
}
106+
li := ui - upper.dp + lower.dp
86107
l := byte('0') // lower digit
87-
if i < lower.nd {
88-
l = lower.d[i]
108+
if li >= 0 && li < lower.nd {
109+
l = lower.d[li]
110+
}
111+
m := byte('0') // middle digit
112+
if mi >= 0 {
113+
m = d.d[mi]
89114
}
90-
m := d.d[i] // middle digit
91115
u := byte('0') // upper digit
92-
if i < upper.nd {
93-
u = upper.d[i]
116+
if ui < upper.nd {
117+
u = upper.d[ui]
94118
}
95119

96120
// Okay to round down (truncate) if lower has a different digit
97121
// or if lower is inclusive and is exactly the result of rounding
98122
// down (i.e., and we have reached the final digit of lower).
99-
okdown := l != m || inclusive && i+1 == lower.nd
123+
okdown := l != m || inclusive && li+1 == lower.nd
100124

125+
switch {
126+
case upperdelta == 0 && m+1 < u:
127+
// Example:
128+
// m = 12345xxx
129+
// u = 12347xxx
130+
upperdelta = 2
131+
case upperdelta == 0 && m != u:
132+
// Example:
133+
// m = 12345xxx
134+
// u = 12346xxx
135+
upperdelta = 1
136+
case upperdelta == 1 && (m != '9' || u != '0'):
137+
// Example:
138+
// m = 1234598x
139+
// u = 1234600x
140+
upperdelta = 2
141+
}
101142
// Okay to round up if upper has a different digit and either upper
102143
// is inclusive or upper is bigger than the result of rounding up.
103-
okup := m != u && (inclusive || m+1 < u || i+1 < upper.nd)
144+
okup := upperdelta > 0 && (inclusive || upperdelta > 1 || ui+1 < upper.nd)
104145

105146
// If it's okay to do either, then round to the nearest one.
106147
// If it's okay to do only one, do it.
107148
switch {
108149
case okdown && okup:
109-
d.Round(i + 1)
150+
d.Round(mi + 1)
110151
return
111152
case okdown:
112-
d.RoundDown(i + 1)
153+
d.RoundDown(mi + 1)
113154
return
114155
case okup:
115-
d.RoundUp(i + 1)
156+
d.RoundUp(mi + 1)
116157
return
117158
}
118159
}

0 commit comments

Comments
 (0)