You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
4.3 KiB
204 lines
4.3 KiB
// Copyright 2015 PingCAP, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package types
|
|
|
|
import (
|
|
"math"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"github.com/pingcap/errors"
|
|
)
|
|
|
|
// RoundFloat rounds float val to the nearest integer value with float64 format, like MySQL Round function.
|
|
// RoundFloat uses default rounding mode, see https://dev.mysql.com/doc/refman/5.7/en/precision-math-rounding.html
|
|
// so rounding use "round half away from zero".
|
|
// e.g, 1.5 -> 2, -1.5 -> -2.
|
|
func RoundFloat(f float64) float64 {
|
|
if math.Abs(f) < 0.5 {
|
|
return 0
|
|
}
|
|
|
|
return math.Trunc(f + math.Copysign(0.5, f))
|
|
}
|
|
|
|
// Round rounds the argument f to dec decimal places.
|
|
// dec defaults to 0 if not specified. dec can be negative
|
|
// to cause dec digits left of the decimal point of the
|
|
// value f to become zero.
|
|
func Round(f float64, dec int) float64 {
|
|
shift := math.Pow10(dec)
|
|
tmp := f * shift
|
|
if math.IsInf(tmp, 0) {
|
|
return f
|
|
}
|
|
result := RoundFloat(tmp) / shift
|
|
if math.IsNaN(result) {
|
|
return 0
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Truncate truncates the argument f to dec decimal places.
|
|
// dec defaults to 0 if not specified. dec can be negative
|
|
// to cause dec digits left of the decimal point of the
|
|
// value f to become zero.
|
|
func Truncate(f float64, dec int) float64 {
|
|
shift := math.Pow10(dec)
|
|
tmp := f * shift
|
|
if math.IsInf(tmp, 0) {
|
|
return f
|
|
}
|
|
return math.Trunc(tmp) / shift
|
|
}
|
|
|
|
// GetMaxFloat gets the max float for given flen and decimal.
|
|
func GetMaxFloat(flen int, decimal int) float64 {
|
|
intPartLen := flen - decimal
|
|
f := math.Pow10(intPartLen)
|
|
f -= math.Pow10(-decimal)
|
|
return f
|
|
}
|
|
|
|
// TruncateFloat tries to truncate f.
|
|
// If the result exceeds the max/min float that flen/decimal allowed, returns the max/min float allowed.
|
|
func TruncateFloat(f float64, flen int, decimal int) (float64, error) {
|
|
if math.IsNaN(f) {
|
|
// nan returns 0
|
|
return 0, ErrOverflow.GenWithStackByArgs("DOUBLE", "")
|
|
}
|
|
|
|
maxF := GetMaxFloat(flen, decimal)
|
|
|
|
if !math.IsInf(f, 0) {
|
|
f = Round(f, decimal)
|
|
}
|
|
|
|
var err error
|
|
if f > maxF {
|
|
f = maxF
|
|
err = ErrOverflow.GenWithStackByArgs("DOUBLE", "")
|
|
} else if f < -maxF {
|
|
f = -maxF
|
|
err = ErrOverflow.GenWithStackByArgs("DOUBLE", "")
|
|
}
|
|
|
|
return f, errors.Trace(err)
|
|
}
|
|
|
|
func isSpace(c byte) bool {
|
|
return c == ' ' || c == '\t'
|
|
}
|
|
|
|
func isDigit(c byte) bool {
|
|
return c >= '0' && c <= '9'
|
|
}
|
|
|
|
// Returns true if the given byte is an ASCII punctuation character (printable and non-alphanumeric).
|
|
func isPunctuation(c byte) bool {
|
|
return (c >= 0x21 && c <= 0x2F) || (c >= 0x3A && c <= 0x40) || (c >= 0x5B && c <= 0x60) || (c >= 0x7B && c <= 0x7E)
|
|
}
|
|
|
|
func myMax(a, b int) int {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func myMaxInt8(a, b int8) int8 {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func myMin(a, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func myMinInt8(a, b int8) int8 {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
const (
|
|
maxUint = uint64(math.MaxUint64)
|
|
uintCutOff = maxUint/uint64(10) + 1
|
|
intCutOff = uint64(math.MaxInt64) + 1
|
|
)
|
|
|
|
// strToInt converts a string to an integer in best effort.
|
|
func strToInt(str string) (int64, error) {
|
|
str = strings.TrimSpace(str)
|
|
if len(str) == 0 {
|
|
return 0, ErrTruncated
|
|
}
|
|
negative := false
|
|
i := 0
|
|
if str[i] == '-' {
|
|
negative = true
|
|
i++
|
|
} else if str[i] == '+' {
|
|
i++
|
|
}
|
|
|
|
var (
|
|
err error
|
|
hasNum = false
|
|
)
|
|
r := uint64(0)
|
|
for ; i < len(str); i++ {
|
|
if !unicode.IsDigit(rune(str[i])) {
|
|
err = ErrTruncated
|
|
break
|
|
}
|
|
hasNum = true
|
|
if r >= uintCutOff {
|
|
r = 0
|
|
err = errors.Trace(ErrBadNumber)
|
|
break
|
|
}
|
|
r = r * uint64(10)
|
|
|
|
r1 := r + uint64(str[i]-'0')
|
|
if r1 < r || r1 > maxUint {
|
|
r = 0
|
|
err = errors.Trace(ErrBadNumber)
|
|
break
|
|
}
|
|
r = r1
|
|
}
|
|
if !hasNum {
|
|
err = ErrTruncated
|
|
}
|
|
|
|
if !negative && r >= intCutOff {
|
|
return math.MaxInt64, errors.Trace(ErrBadNumber)
|
|
}
|
|
|
|
if negative && r > intCutOff {
|
|
return math.MinInt64, errors.Trace(ErrBadNumber)
|
|
}
|
|
|
|
if negative {
|
|
r = -r
|
|
}
|
|
return int64(r), err
|
|
}
|
|
|