github.com/dubbogo/gost@v1.14.0/math/big/helper.go (about)

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