// Copyright 2017 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.//go:generate stringer -type RoundingModepackage numberimport ()// RoundingMode determines how a number is rounded to the desired precision.typeRoundingModebyteconst (ToNearestEvenRoundingMode = iota// towards the nearest integer, or towards an even number if equidistant.ToNearestZero// towards the nearest integer, or towards zero if equidistant.ToNearestAway// towards the nearest integer, or away from zero if equidistant.ToPositiveInf// towards infinityToNegativeInf// towards negative infinityToZero// towards zeroAwayFromZero// away from zero numModes)const maxIntDigits = 20// A Decimal represents a floating point number in decimal format.// Digits represents a number [0, 1.0), and the absolute value represented by// Decimal is Digits * 10^Exp. Leading and trailing zeros may be omitted and Exp// may point outside a valid position in Digits.//// Examples://// Number Decimal// 12345 Digits: [1, 2, 3, 4, 5], Exp: 5// 12.345 Digits: [1, 2, 3, 4, 5], Exp: 2// 12000 Digits: [1, 2], Exp: 5// 12000.00 Digits: [1, 2], Exp: 5// 0.00123 Digits: [1, 2, 3], Exp: -2// 0 Digits: [], Exp: 0typeDecimalstruct {digits buf [maxIntDigits]byte}type digits struct { Digits []byte// mantissa digits, big-endian Exp int32// exponent Neg bool Inf bool// Takes precedence over Digits and Exp. NaN bool// Takes precedence over Inf.}// Digits represents a floating point number represented in digits of the// base in which a number is to be displayed. It is similar to Decimal, but// keeps track of trailing fraction zeros and the comma placement for// engineering notation. Digits must have at least one digit.//// Examples://// Number Decimal// decimal// 12345 Digits: [1, 2, 3, 4, 5], Exp: 5 End: 5// 12.345 Digits: [1, 2, 3, 4, 5], Exp: 2 End: 5// 12000 Digits: [1, 2], Exp: 5 End: 5// 12000.00 Digits: [1, 2], Exp: 5 End: 7// 0.00123 Digits: [1, 2, 3], Exp: -2 End: 3// 0 Digits: [], Exp: 0 End: 1// scientific (actual exp is Exp - Comma)// 0e0 Digits: [0], Exp: 1, End: 1, Comma: 1// .0e0 Digits: [0], Exp: 0, End: 1, Comma: 0// 0.0e0 Digits: [0], Exp: 1, End: 2, Comma: 1// 1.23e4 Digits: [1, 2, 3], Exp: 5, End: 3, Comma: 1// .123e5 Digits: [1, 2, 3], Exp: 5, End: 3, Comma: 0// engineering// 12.3e3 Digits: [1, 2, 3], Exp: 5, End: 3, Comma: 2typeDigitsstruct {digits// End indicates the end position of the number. End int32// For decimals Exp <= End. For scientific len(Digits) <= End.// Comma is used for the comma position for scientific (always 0 or 1) and // engineering notation (always 0, 1, 2, or 3). Comma uint8// IsScientific indicates whether this number is to be rendered as a // scientific number. IsScientific bool}func ( *Digits) () int {if .Exp >= .End {return0 }returnint(.End - .Exp)}// normalize returns a new Decimal with leading and trailing zeros removed.func ( *Decimal) () ( Decimal) { = * := .Digits// Strip leading zeros. Resulting number of digits is significant digits.forlen() > 0 && [0] == 0 { = [1:] .Exp-- }// Strip trailing zerosforlen() > 0 && [len()-1] == 0 { = [:len()-1] }iflen() == 0 { .Exp = 0 } .Digits = return}func ( *Decimal) () { := .Digitsif == nil { = .buf[:0] } * = Decimal{} .Digits = [:0]}func ( *Decimal) () string {if .NaN {return"NaN" }var []byteif .Neg { = append(, '-') }if .Inf { = append(, "Inf"...)returnstring() }switch {caselen(.Digits) == 0: = append(, '0')case .Exp <= 0:// 0.00ddd = append(, "0."...) = appendZeros(, -int(.Exp)) = appendDigits(, .Digits)case/* 0 < */int(.Exp) < len(.Digits):// dd.ddd = appendDigits(, .Digits[:.Exp]) = append(, '.') = appendDigits(, .Digits[.Exp:])default: // len(x.Digits) <= x.Exp// ddd00 = appendDigits(, .Digits) = appendZeros(, int(.Exp)-len(.Digits)) }returnstring()}func appendDigits( []byte, []byte) []byte {for , := range { = append(, +'0') }return}// appendZeros appends n 0 digits to buf and returns buf.func appendZeros( []byte, int) []byte {for ; > 0; -- { = append(, '0') }return}func ( *digits) ( RoundingMode, int) {if >= len(.Digits) {return }// Make rounding decision: The result mantissa is truncated ("rounded down") // by default. Decide if we need to increment, or "round up", the (unsigned) // mantissa. := falseswitch {caseToNegativeInf: = .NegcaseToPositiveInf: = !.NegcaseToZero:// nothing to docaseAwayFromZero: = truecaseToNearestEven: = .Digits[] > 5 || .Digits[] == 5 && (len(.Digits) > +1 || == 0 || .Digits[-1]&1 != 0)caseToNearestAway: = .Digits[] >= 5caseToNearestZero: = .Digits[] > 5 || .Digits[] == 5 && len(.Digits) > +1default:panic("unreachable") }if { .roundUp() } else { .roundDown() }}// roundFloat rounds a floating point number.func ( RoundingMode) ( float64) float64 {// Make rounding decision: The result mantissa is truncated ("rounded down") // by default. Decide if we need to increment, or "round up", the (unsigned) // mantissa. := if < 0 { = - } , := math.Modf()if == 0.0 {return } := falseswitch {caseToNegativeInf: = < 0caseToPositiveInf: = >= 0caseToZero:// nothing to docaseAwayFromZero: = truecaseToNearestEven:// TODO: check overflow = > 0.5 || == 0.5 && int64()&1 != 0caseToNearestAway: = >= 0.5caseToNearestZero: = > 0.5default:panic("unreachable") }if { += 1 }if != { = - }return}func ( *digits) ( int) {if < 0 || >= len(.Digits) {return// nothing to do }// find first digit < 9for > 0 && .Digits[-1] >= 9 { -- }if == 0 {// all digits are 9s => round up to 1 and update exponent .Digits[0] = 1// ok since len(x.Digits) > n .Digits = .Digits[:1] .Exp++return } .Digits[-1]++ .Digits = .Digits[:]// x already trimmed}func ( *digits) ( int) {if < 0 || >= len(.Digits) {return// nothing to do } .Digits = .Digits[:]trim()}// trim cuts off any trailing zeros from x's mantissa;// they are meaningless for the value of x.func trim( *digits) { := len(.Digits)for > 0 && .Digits[-1] == 0 { -- } .Digits = .Digits[:]if == 0 { .Exp = 0 }}// A Converter converts a number into decimals according to the given rounding// criteria.typeConverterinterface {Convert(d *Decimal, r RoundingContext)}const ( signed = true unsigned = false)// Convert converts the given number to the decimal representation using the// supplied RoundingContext.func ( *Decimal) ( RoundingContext, interface{}) {switch f := .(type) {caseConverter: .clear() .Convert(, )casefloat32: .ConvertFloat(, float64(), 32)casefloat64: .ConvertFloat(, , 64)caseint: .ConvertInt(, signed, uint64())caseint8: .ConvertInt(, signed, uint64())caseint16: .ConvertInt(, signed, uint64())caseint32: .ConvertInt(, signed, uint64())caseint64: .ConvertInt(, signed, uint64())caseuint: .ConvertInt(, unsigned, uint64())caseuint8: .ConvertInt(, unsigned, uint64())caseuint16: .ConvertInt(, unsigned, uint64())caseuint32: .ConvertInt(, unsigned, uint64())caseuint64: .ConvertInt(, unsigned, )default: .NaN = true// TODO: // case string: if produced by strconv, allows for easy arbitrary pos. // case reflect.Value: // case big.Float // case big.Int // case big.Rat? // catch underlyings using reflect or will this already be done by the // message package? }}// ConvertInt converts an integer to decimals.func ( *Decimal) ( RoundingContext, bool, uint64) {if .Increment > 0 {// TODO: if uint64 is too large, fall back to float64if { .ConvertFloat(, float64(int64()), 64) } else { .ConvertFloat(, float64(), 64) }return } .clear()if && int64() < 0 { = uint64(-int64()) .Neg = true } .fillIntDigits() .Exp = int32(len(.Digits))}// ConvertFloat converts a floating point number to decimals.func ( *Decimal) ( RoundingContext, float64, int) { .clear()ifmath.IsNaN() { .NaN = truereturn }// Simple case: decimal notationif .Increment > 0 { := int(.IncrementScale) := 1.0if >= len(scales) { = math.Pow(10, float64()) } else { = scales[] }// We multiply x instead of dividing inc as it gives less rounding // issues. *= /= float64(.Increment) = .Mode.roundFloat() *= float64(.Increment) /= } := if < 0 { .Neg = true = - }ifmath.IsInf(, 1) { .Inf = truereturn }// By default we get the exact decimal representation. := byte('g') := -1// As the strconv API does not return the rounding accuracy, we can only // round using ToNearestEven.if .Mode == ToNearestEven {if := .RoundSignificantDigits(); >= 0 { = } elseif = .RoundFractionDigits(); >= 0 { = = 'f' } } else {// TODO: At this point strconv's rounding is imprecise to the point that // it is not usable for this purpose. // See https://github.com/golang/go/issues/21714 // If rounding is requested, we ask for a large number of digits and // round from there to simulate rounding only once. // Ideally we would have strconv export an AppendDigits that would take // a rounding mode and/or return an accuracy. Something like this would // work: // AppendDigits(dst []byte, x float64, base, size, prec int) (digits []byte, exp, accuracy int) := .RoundSignificantDigits() >= 0 := .RoundFractionDigits() >= 0if || {// prec is the number of mantissa bits plus some extra for safety. // We need at least the number of mantissa bits as decimals to // accurately represent the floating point without rounding, as each // bit requires one more decimal to represent: 0.5, 0.25, 0.125, ... = 60 } } := strconv.AppendFloat(.Digits[:0], , , , ) := 0 := 0 := 1for < len() {if := []; '0' <= && <= '9' { [] = - '0' ++ .Exp += int32() } elseif == '.' { = 0 .Exp = int32() } else {break } ++ } .Digits = [:]if != len() { += len("e") := := 0for ++; < len(); ++ { *= 10 += int([] - '0') }if [] == '-' { = - } .Exp = int32() + 1 }}func ( *Decimal) ( uint64) {ifcap(.Digits) < maxIntDigits { .Digits = .buf[:] } else { .Digits = .buf[:maxIntDigits] } := 0for ; > 0; /= 10 { .Digits[] = byte( % 10) ++ } .Digits = .Digits[:]for := 0; < ; ++ { -- .Digits[], .Digits[] = .Digits[], .Digits[] }}var scales [70]float64func init() { := 1.0for := rangescales {scales[] = *= 10 }}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.