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  }