github.com/XiaoMi/Gaea@v1.2.5/parser/tidb-types/helper.go (about)

     1  // Copyright 2015 PingCAP, Inc.
     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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package types
    15  
    16  import (
    17  	"math"
    18  	"strings"
    19  	"unicode"
    20  
    21  	"github.com/pingcap/errors"
    22  )
    23  
    24  // RoundFloat rounds float val to the nearest integer value with float64 format, like MySQL Round function.
    25  // RoundFloat uses default rounding mode, see https://dev.mysql.com/doc/refman/5.7/en/precision-math-rounding.html
    26  // so rounding use "round half away from zero".
    27  // e.g, 1.5 -> 2, -1.5 -> -2.
    28  func RoundFloat(f float64) float64 {
    29  	if math.Abs(f) < 0.5 {
    30  		return 0
    31  	}
    32  
    33  	return math.Trunc(f + math.Copysign(0.5, f))
    34  }
    35  
    36  // Round rounds the argument f to dec decimal places.
    37  // dec defaults to 0 if not specified. dec can be negative
    38  // to cause dec digits left of the decimal point of the
    39  // value f to become zero.
    40  func Round(f float64, dec int) float64 {
    41  	shift := math.Pow10(dec)
    42  	tmp := f * shift
    43  	if math.IsInf(tmp, 0) {
    44  		return f
    45  	}
    46  	return RoundFloat(tmp) / shift
    47  }
    48  
    49  // Truncate truncates the argument f to dec decimal places.
    50  // dec defaults to 0 if not specified. dec can be negative
    51  // to cause dec digits left of the decimal point of the
    52  // value f to become zero.
    53  func Truncate(f float64, dec int) float64 {
    54  	shift := math.Pow10(dec)
    55  	tmp := f * shift
    56  	if math.IsInf(tmp, 0) {
    57  		return f
    58  	}
    59  	return math.Trunc(tmp) / shift
    60  }
    61  
    62  // GetMaxFloat gets the max float for given flen and decimal.
    63  func GetMaxFloat(flen int, decimal int) float64 {
    64  	intPartLen := flen - decimal
    65  	f := math.Pow10(intPartLen)
    66  	f -= math.Pow10(-decimal)
    67  	return f
    68  }
    69  
    70  // TruncateFloat tries to truncate f.
    71  // If the result exceeds the max/min float that flen/decimal allowed, returns the max/min float allowed.
    72  func TruncateFloat(f float64, flen int, decimal int) (float64, error) {
    73  	if math.IsNaN(f) {
    74  		// nan returns 0
    75  		return 0, ErrOverflow.GenWithStackByArgs("DOUBLE", "")
    76  	}
    77  
    78  	maxF := GetMaxFloat(flen, decimal)
    79  
    80  	if !math.IsInf(f, 0) {
    81  		f = Round(f, decimal)
    82  	}
    83  
    84  	var err error
    85  	if f > maxF {
    86  		f = maxF
    87  		err = ErrOverflow.GenWithStackByArgs("DOUBLE", "")
    88  	} else if f < -maxF {
    89  		f = -maxF
    90  		err = ErrOverflow.GenWithStackByArgs("DOUBLE", "")
    91  	}
    92  
    93  	return f, errors.Trace(err)
    94  }
    95  
    96  func isSpace(c byte) bool {
    97  	return c == ' ' || c == '\t'
    98  }
    99  
   100  func isDigit(c byte) bool {
   101  	return c >= '0' && c <= '9'
   102  }
   103  
   104  func myMax(a, b int) int {
   105  	if a > b {
   106  		return a
   107  	}
   108  	return b
   109  }
   110  
   111  func myMaxInt8(a, b int8) int8 {
   112  	if a > b {
   113  		return a
   114  	}
   115  	return b
   116  }
   117  
   118  func myMin(a, b int) int {
   119  	if a < b {
   120  		return a
   121  	}
   122  	return b
   123  }
   124  
   125  func myMinInt8(a, b int8) int8 {
   126  	if a < b {
   127  		return a
   128  	}
   129  	return b
   130  }
   131  
   132  const (
   133  	maxUint    = uint64(math.MaxUint64)
   134  	uintCutOff = maxUint/uint64(10) + 1
   135  	intCutOff  = uint64(math.MaxInt64) + 1
   136  )
   137  
   138  // strToInt converts a string to an integer in best effort.
   139  func strToInt(str string) (int64, error) {
   140  	str = strings.TrimSpace(str)
   141  	if len(str) == 0 {
   142  		return 0, ErrTruncated
   143  	}
   144  	negative := false
   145  	i := 0
   146  	if str[i] == '-' {
   147  		negative = true
   148  		i++
   149  	} else if str[i] == '+' {
   150  		i++
   151  	}
   152  
   153  	var (
   154  		err    error
   155  		hasNum = false
   156  	)
   157  	r := uint64(0)
   158  	for ; i < len(str); i++ {
   159  		if !unicode.IsDigit(rune(str[i])) {
   160  			err = ErrTruncated
   161  			break
   162  		}
   163  		hasNum = true
   164  		if r >= uintCutOff {
   165  			r = 0
   166  			err = errors.Trace(ErrBadNumber)
   167  			break
   168  		}
   169  		r = r * uint64(10)
   170  
   171  		r1 := r + uint64(str[i]-'0')
   172  		if r1 < r || r1 > maxUint {
   173  			r = 0
   174  			err = errors.Trace(ErrBadNumber)
   175  			break
   176  		}
   177  		r = r1
   178  	}
   179  	if !hasNum {
   180  		err = ErrTruncated
   181  	}
   182  
   183  	if !negative && r >= intCutOff {
   184  		return math.MaxInt64, errors.Trace(ErrBadNumber)
   185  	}
   186  
   187  	if negative && r > intCutOff {
   188  		return math.MinInt64, errors.Trace(ErrBadNumber)
   189  	}
   190  
   191  	if negative {
   192  		r = -r
   193  	}
   194  	return int64(r), err
   195  }