github.com/matrixorigin/matrixone@v0.7.0/pkg/vectorize/round/round.go (about) 1 // Copyright 2021 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package round 16 17 /* round package provides rounding function for all numeric types(uint8, uint16, uint32, uint64, int8, int16, int32, int64, float32, float64). 18 the round functions here round numbers using the banker's rule, that is, round to the nearest even number in the situation of .5, 19 1.5 ----> 2 20 2.5 ----> 2 21 3.5 ----> 4 and so on. 22 caveat: for integer numbers, overflow can happen and it's behavior is undefined. 23 round function takes one or two parameters as its argument, and the second argument must be a constant. 24 round(x, N) 25 round(x) == round(x, 0) 26 N < 0, N zeroes in front of decimal point 27 N >= 0, round to the Nth placeholder after decimal point 28 */ 29 30 import ( 31 "math" 32 33 "github.com/matrixorigin/matrixone/pkg/common/moerr" 34 35 "github.com/matrixorigin/matrixone/pkg/vectorize/floor" 36 ) 37 38 var ( 39 RoundUint8 func([]uint8, []uint8, int64) []uint8 40 RoundUint16 func([]uint16, []uint16, int64) []uint16 41 RoundUint32 func([]uint32, []uint32, int64) []uint32 42 RoundUint64 func([]uint64, []uint64, int64) []uint64 43 RoundInt8 func([]int8, []int8, int64) []int8 44 RoundInt16 func([]int16, []int16, int64) []int16 45 RoundInt32 func([]int32, []int32, int64) []int32 46 RoundInt64 func([]int64, []int64, int64) []int64 47 RoundFloat32 func([]float32, []float32, int64) []float32 48 RoundFloat64 func([]float64, []float64, int64) []float64 49 ) 50 51 func init() { 52 RoundUint8 = roundUint8 53 RoundUint16 = roundUint16 54 RoundUint32 = roundUint32 55 RoundUint64 = roundUint64 56 RoundInt8 = roundInt8 57 RoundInt16 = roundInt16 58 RoundInt32 = roundInt32 59 RoundInt64 = roundInt64 60 RoundFloat32 = roundFloat32 61 RoundFloat64 = roundFloat64 62 } 63 64 var maxUint8digits = floor.MaxUint8digits 65 var maxUint16digits = floor.MaxUint16digits 66 var maxUint32digits = floor.MaxUint32digits 67 var maxUint64digits = floor.MaxUint64digits 68 var maxInt8digits = floor.MaxInt8digits 69 var maxInt16digits = floor.MaxInt16digits 70 var maxInt32digits = floor.MaxInt32digits 71 var maxInt64digits = floor.MaxInt64digits 72 73 var scaleTable = floor.ScaleTable 74 75 // roundUint8, roundUint16, roundUint32, roundInt8, roundInt16, roundInt32 are basically orphan code, only called in plan1 and will be deleted soon. 76 func roundUint8(xs []uint8, rs []uint8, digits int64) []uint8 { 77 // maximum uint8 number is 255, so we only need to worry about a few digit cases, 78 switch { 79 case digits >= 0: 80 return xs 81 case digits == -1 || digits == -2: 82 scale := float64(scaleTable[-digits]) 83 ///// 84 85 ///// 86 for i := range xs { 87 value := int((float64(xs[i])+0.5*scale)/scale) * int(scale) //todo(broccoli): please find a better way to round away from zero 88 rs[i] = uint8(value) 89 } 90 case digits <= -maxUint8digits: 91 for i := range xs { 92 rs[i] = 0 93 } 94 } 95 return rs 96 } 97 98 func roundUint16(xs []uint16, rs []uint16, digits int64) []uint16 { 99 switch { 100 case digits >= 0: 101 return xs 102 case digits > -maxUint16digits: 103 scale := float64(scaleTable[-digits]) 104 for i := range xs { 105 value := int((float64(xs[i])+0.5*scale)/scale) * int(scale) //todo(broccoli): please find a better way to round away from zero 106 rs[i] = uint16(value) 107 } 108 case digits <= -maxUint16digits: 109 for i := range xs { 110 rs[i] = 0 111 } 112 } 113 return rs 114 } 115 116 func roundUint32(xs []uint32, rs []uint32, digits int64) []uint32 { 117 switch { 118 case digits >= 0: 119 return xs 120 case digits > -maxUint32digits: 121 scale := float64(scaleTable[-digits]) 122 for i := range xs { 123 value := int((float64(xs[i])+0.5*scale)/scale) * int(scale) //todo(broccoli): please find a better way to round away from zero 124 rs[i] = uint32(value) 125 } 126 case digits <= maxUint32digits: 127 for i := range xs { 128 rs[i] = 0 129 } 130 } 131 return rs 132 } 133 134 func roundUint64(xs []uint64, rs []uint64, digits int64) []uint64 { 135 switch { 136 case digits >= 0: 137 return xs 138 case digits > -maxUint64digits: // round algorithm contributed by @ffftian 139 scale := scaleTable[-digits] 140 for i := range xs { 141 step1 := xs[i] / scale * scale 142 step2 := xs[i] % scale 143 if step2 >= scale/2 { 144 rs[i] = step1 + scale 145 if rs[i] < step1 { 146 panic(moerr.NewOutOfRangeNoCtx("uint64", "ROUND")) 147 } 148 } else { 149 rs[i] = step1 150 } 151 } 152 case digits <= -maxUint64digits: 153 for i := range xs { 154 rs[i] = 0 155 } 156 } 157 return rs 158 } 159 160 func roundInt8(xs []int8, rs []int8, digits int64) []int8 { 161 switch { 162 case digits >= 0: 163 return xs 164 case digits == -1 || digits == -2: 165 scale := float64(scaleTable[-digits]) 166 for i := range xs { 167 if xs[i] > 0 { 168 value := int((float64(xs[i])+0.5*scale)/scale) * int(scale) //todo(broccoli): please find a better way to round away from zero 169 rs[i] = int8(value) 170 } else if xs[i] < 0 { 171 value := int((float64(xs[i])-0.5*scale)/scale) * int(scale) //todo(broccoli): please find a better way to round away from zero 172 rs[i] = int8(value) 173 } else { 174 rs[i] = 0 175 } 176 } 177 case digits <= -maxInt8digits: 178 for i := range xs { 179 rs[i] = 0 180 } 181 } 182 return rs 183 } 184 185 func roundInt16(xs []int16, rs []int16, digits int64) []int16 { 186 switch { 187 case digits >= 0: 188 return xs 189 case digits > -maxInt16digits: 190 scale := float64(scaleTable[-digits]) 191 for i := range xs { 192 if xs[i] > 0 { 193 value := int((float64(xs[i])+0.5*scale)/scale) * int(scale) //todo(broccoli): please find a better way to round away from zero 194 rs[i] = int16(value) 195 } else if xs[i] < 0 { 196 value := int((float64(xs[i])-0.5*scale)/scale) * int(scale) //todo(broccoli): please find a better way to round away from zero 197 rs[i] = int16(value) 198 } else { 199 rs[i] = 0 200 } 201 } 202 case digits <= -maxInt16digits: 203 for i := range xs { 204 rs[i] = 0 205 } 206 } 207 return rs 208 } 209 210 func roundInt32(xs []int32, rs []int32, digits int64) []int32 { 211 switch { 212 case digits >= 0: 213 return xs 214 case digits > -maxInt32digits: 215 scale := float64(scaleTable[-digits]) 216 for i := range xs { 217 if xs[i] > 0 { 218 value := int((float64(xs[i])+0.5*scale)/scale) * int(scale) //todo(broccoli): please find a better way to round away from zero 219 rs[i] = int32(value) 220 } else if xs[i] < 0 { 221 value := int((float64(xs[i])-0.5*scale)/scale) * int(scale) //todo(broccoli): please find a better way to round away from zero 222 rs[i] = int32(value) 223 } else { 224 rs[i] = 0 225 } 226 } 227 case digits <= maxInt32digits: 228 for i := range xs { 229 rs[i] = 0 230 } 231 } 232 return rs 233 } 234 235 func roundInt64(xs []int64, rs []int64, digits int64) []int64 { 236 switch { 237 case digits >= 0: 238 return xs 239 case digits > -maxInt64digits: 240 scale := int64(scaleTable[-digits]) // round algorithm contributed by @ffftian 241 for i := range xs { 242 if xs[i] > 0 { 243 step1 := xs[i] / scale * scale 244 step2 := xs[i] % scale 245 if step2 >= scale/2 { 246 rs[i] = step1 + scale 247 if rs[i] < step1 { 248 panic(moerr.NewOutOfRangeNoCtx("int64", "ROUND")) 249 } 250 } else { 251 rs[i] = step1 252 } 253 } else if xs[i] < 0 { 254 step1 := xs[i] / scale * scale 255 step2 := xs[i] % scale // module operation with negative numbers, the result is negative 256 if step2 <= scale/2 { 257 rs[i] = step1 - scale 258 if rs[i] > step1 { 259 panic(moerr.NewOutOfRangeNoCtx("int64", "ROUND")) 260 } 261 } else { 262 rs[i] = step1 263 } 264 } else { 265 rs[i] = 0 266 } 267 } 268 case digits <= maxInt64digits: 269 for i := range xs { 270 rs[i] = 0 271 } 272 } 273 return rs 274 } 275 276 func roundFloat32(xs []float32, rs []float32, digits int64) []float32 { 277 if digits == 0 { 278 for i := range xs { 279 rs[i] = float32(math.RoundToEven(float64(xs[i]))) 280 } 281 } else if digits >= 38 { // the range of float32 e-38 ~ e38 282 copy(rs, xs) 283 } else if digits <= -38 { 284 for i := range xs { 285 rs[i] = 0 286 } 287 } else { 288 scale := math.Pow10(int(digits)) 289 for i := range xs { 290 value := float64(xs[i]) * scale 291 roundResult := math.RoundToEven(value) 292 rs[i] = float32(roundResult / scale) 293 } 294 } 295 return rs 296 } 297 298 func roundFloat64(xs []float64, rs []float64, digits int64) []float64 { 299 if digits == 0 { 300 for i := range xs { 301 rs[i] = math.RoundToEven(xs[i]) 302 } 303 } else if digits >= 308 { // the range of float64 304 copy(rs, xs) 305 } else if digits <= -308 { 306 for i := range xs { 307 rs[i] = 0 308 } 309 } else { 310 var abs_digits uint64 311 if digits < 0 { 312 abs_digits = uint64(-digits) 313 } else { 314 abs_digits = uint64(digits) 315 } 316 var tmp = math.Pow(10.0, float64(abs_digits)) 317 318 if digits > 0 { 319 for i := range xs { 320 var value_mul_tmp = xs[i] * tmp 321 rs[i] = math.RoundToEven(value_mul_tmp) / tmp 322 } 323 } else { 324 for i := range xs { 325 var value_div_tmp = xs[i] / tmp 326 rs[i] = math.RoundToEven(value_div_tmp) * tmp 327 } 328 } 329 } 330 return rs 331 }