@@ -1263,7 +1263,7 @@ var Builtins = map[string][]Builtin{
1263
1263
1264
1264
"round" : {
1265
1265
floatBuiltin1 (func (x float64 ) (Datum , error ) {
1266
- return round (x , 0 )
1266
+ return NewDFloat ( DFloat ( round (x ))), nil
1267
1267
}, "Rounds `val` to the nearest integer using half to even (banker's) rounding." ),
1268
1268
decimalBuiltin1 (func (x * apd.Decimal ) (Datum , error ) {
1269
1269
return roundDecimal (x , 0 )
@@ -1273,7 +1273,25 @@ var Builtins = map[string][]Builtin{
1273
1273
Types : ArgTypes {{"input" , TypeFloat }, {"decimal_accuracy" , TypeInt }},
1274
1274
ReturnType : fixedReturnType (TypeFloat ),
1275
1275
fn : func (_ * EvalContext , args Datums ) (Datum , error ) {
1276
- return round (float64 (* args [0 ].(* DFloat )), int64 (MustBeDInt (args [1 ])))
1276
+ var x apd.Decimal
1277
+ if _ , err := x .SetFloat64 (float64 (* args [0 ].(* DFloat ))); err != nil {
1278
+ return nil , err
1279
+ }
1280
+
1281
+ // TODO(mjibson): make sure this fits in an int32.
1282
+ scale := int32 (MustBeDInt (args [1 ]))
1283
+
1284
+ var d apd.Decimal
1285
+ if _ , err := RoundCtx .Quantize (& d , & x , - scale ); err != nil {
1286
+ return nil , err
1287
+ }
1288
+
1289
+ f , err := d .Float64 ()
1290
+ if err != nil {
1291
+ return nil , err
1292
+ }
1293
+
1294
+ return NewDFloat (DFloat (f )), nil
1277
1295
},
1278
1296
Info : "Keeps `decimal_accuracy` number of figures to the right of the zero position " +
1279
1297
" in `input` using half to even (banker's) rounding." ,
@@ -2082,36 +2100,72 @@ func overlay(s, to string, pos, size int) (Datum, error) {
2082
2100
return NewDString (string (runes [:pos ]) + to + string (runes [after :])), nil
2083
2101
}
2084
2102
2085
- func round (x float64 , n int64 ) (Datum , error ) {
2086
- pow := math .Pow (10 , float64 (n ))
2103
+ // Transcribed from Postgres' src/port/rint.c, with c-style comments preserved
2104
+ // for ease of mapping.
2105
+ //
2106
+ // https://github.com/postgres/postgres/blob/REL9_6_3/src/port/rint.c
2107
+ func round (x float64 ) float64 {
2108
+ /* Per POSIX, NaNs must be returned unchanged. */
2109
+ if math .IsNaN (x ) {
2110
+ return x
2111
+ }
2087
2112
2088
- if pow == 0 {
2089
- // Rounding to so many digits on the left that we're underflowing.
2090
- // Avoid a NaN below.
2091
- return NewDFloat (DFloat (0 )), nil
2113
+ /* Both positive and negative zero should be returned unchanged. */
2114
+ if x == 0.0 {
2115
+ return x
2092
2116
}
2093
- if math . Abs ( x * pow ) > 1e17 {
2094
- // Rounding touches decimals below float precision; the operation
2095
- // is a no-op.
2096
- return NewDFloat ( DFloat ( x )), nil
2117
+
2118
+ roundFn := math . Ceil
2119
+ if math . Signbit ( x ) {
2120
+ roundFn = math . Floor
2097
2121
}
2098
2122
2099
- v , frac := math .Modf (x * pow )
2100
- // The following computation implements unbiased rounding, also
2101
- // called bankers' rounding. It ensures that values that fall
2102
- // exactly between two integers get equal chance to be rounded up or
2103
- // down.
2104
- if x > 0.0 {
2105
- if frac > 0.5 || (frac == 0.5 && uint64 (v )% 2 != 0 ) {
2106
- v += 1.0
2107
- }
2108
- } else {
2109
- if frac < - 0.5 || (frac == - 0.5 && uint64 (v )% 2 != 0 ) {
2110
- v -= 1.0
2111
- }
2123
+ /*
2124
+ * Subtracting 0.5 from a number very close to -0.5 can round to
2125
+ * exactly -1.0, producing incorrect results, so we take the opposite
2126
+ * approach: add 0.5 to the negative number, so that it goes closer to
2127
+ * zero (or at most to +0.5, which is dealt with next), avoiding the
2128
+ * precision issue.
2129
+ */
2130
+ xOrig := x
2131
+ x -= math .Copysign (0.5 , x )
2132
+
2133
+ /*
2134
+ * Be careful to return minus zero when input+0.5 >= 0, as that's what
2135
+ * rint() should return with negative input.
2136
+ */
2137
+ if x == 0 || math .Signbit (x ) != math .Signbit (xOrig ) {
2138
+ return math .Copysign (0.0 , xOrig )
2139
+ }
2140
+
2141
+ /*
2142
+ * For very big numbers the input may have no decimals. That case is
2143
+ * detected by testing x+0.5 == x+1.0; if that happens, the input is
2144
+ * returned unchanged. This also covers the case of minus infinity.
2145
+ */
2146
+ if x == xOrig - math .Copysign (1.0 , x ) {
2147
+ return xOrig
2148
+ }
2149
+
2150
+ /* Otherwise produce a rounded estimate. */
2151
+ r := roundFn (x )
2152
+
2153
+ /*
2154
+ * If the rounding did not produce exactly input+0.5 then we're done.
2155
+ */
2156
+ if r != x {
2157
+ return r
2112
2158
}
2113
2159
2114
- return NewDFloat (DFloat (v / pow )), nil
2160
+ /*
2161
+ * The original fractional part was exactly 0.5 (since
2162
+ * floor(input+0.5) == input+0.5). We need to round to nearest even.
2163
+ * Dividing input+0.5 by 2, taking the floor and multiplying by 2
2164
+ * yields the closest even number. This part assumes that division by
2165
+ * 2 is exact, which should be OK because underflow is impossible
2166
+ * here: x is an integer.
2167
+ */
2168
+ return roundFn (x * 0.5 ) * 2.0
2115
2169
}
2116
2170
2117
2171
func roundDecimal (x * apd.Decimal , n int32 ) (Datum , error ) {
0 commit comments