Source File
ratconv.go
Belonging Package
math/big
// Copyright 2015 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.// This file implements rat-to-string conversion functions.package bigimport ()func ( rune) bool {return strings.ContainsRune("+-/0123456789.eE", )}var ratZero Ratvar _ fmt.Scanner = &ratZero // *Rat must implement fmt.Scanner// Scan is a support routine for fmt.Scanner. It accepts the formats// 'e', 'E', 'f', 'F', 'g', 'G', and 'v'. All formats are equivalent.func ( *Rat) ( fmt.ScanState, rune) error {, := .Token(true, ratTok)if != nil {return}if !strings.ContainsRune("efgEFGv", ) {return errors.New("Rat.Scan: invalid verb")}if , := .SetString(string()); ! {return errors.New("Rat.Scan: invalid syntax")}return nil}// SetString sets z to the value of s and returns z and a boolean indicating// success. s can be given as a (possibly signed) fraction "a/b", or as a// floating-point number optionally followed by an exponent.// If a fraction is provided, both the dividend and the divisor may be a// decimal integer or independently use a prefix of “0b”, “0” or “0o”,// or “0x” (or their upper-case variants) to denote a binary, octal, or// hexadecimal integer, respectively. The divisor may not be signed.// If a floating-point number is provided, it may be in decimal form or// use any of the same prefixes as above but for “0” to denote a non-decimal// mantissa. A leading “0” is considered a decimal leading 0; it does not// indicate octal representation in this case.// An optional base-10 “e” or base-2 “p” (or their upper-case variants)// exponent may be provided as well, except for hexadecimal floats which// only accept an (optional) “p” exponent (because an “e” or “E” cannot// be distinguished from a mantissa digit). If the exponent's absolute value// is too large, the operation may fail.// The entire string, not just a prefix, must be valid for success. If the// operation failed, the value of z is undefined but the returned value is nil.func ( *Rat) ( string) (*Rat, bool) {if len() == 0 {return nil, false}// len(s) > 0// parse fraction a/b, if anyif := strings.Index(, "/"); >= 0 {if , := .a.SetString([:], 0); ! {return nil, false}:= strings.NewReader([+1:])var errorif .b.abs, _, _, = .b.abs.scan(, 0, false); != nil {return nil, false}// entire string must have been consumedif _, = .ReadByte(); != io.EOF {return nil, false}if len(.b.abs) == 0 {return nil, false}return .norm(), true}// parse floating-point number:= strings.NewReader()// sign, := scanSign()if != nil {return nil, false}// mantissavar intvar int // fractional digit count; valid if <= 0.a.abs, , , = .a.abs.scan(, 0, true)if != nil {return nil, false}// exponentvar int64var int, , = scanExponent(, true, true)if != nil {return nil, false}// there should be no unread characters leftif _, = .ReadByte(); != io.EOF {return nil, false}// special-case 0 (see also issue #16176)if len(.a.abs) == 0 {return .norm(), true}// len(z.a.abs) > 0// The mantissa may have a radix point (fcount <= 0) and there// may be a nonzero exponent exp. The radix point amounts to a// division by base**(-fcount), which equals a multiplication by// base**fcount. An exponent means multiplication by ebase**exp.// Multiplications are commutative, so we can apply them in any// order. We only have powers of 2 and 10, and we split powers// of 10 into the product of the same powers of 2 and 5. This// may reduce the size of shift/multiplication factors or// divisors required to create the final fraction, depending// on the actual floating-point value.// determine binary or decimal exponent contribution of radix pointvar , int64if < 0 {// The mantissa has a radix point ddd.dddd; and// -fcount is the number of digits to the right// of '.'. Adjust relevant exponent accordingly.:= int64()switch {case 10:=fallthrough // 10**e == 5**e * 2**ecase 2:=case 8:= * 3 // octal digits are 3 bits eachcase 16:= * 4 // hexadecimal digits are 4 bits eachdefault:panic("unexpected mantissa base")}// fcount consumed - not needed anymore}// take actual exponent into accountswitch {case 10:+=fallthrough // see fallthrough abovecase 2:+=default:panic("unexpected exponent base")}// exp consumed - not needed anymore// apply exp5 contributions// (start with exp5 so the numbers to multiply are smaller)if != 0 {:=if < 0 {= -if < 0 {// This can occur if -n overflows. -(-1 << 63) would become// -1 << 63, which is still negative.return nil, false}}if > 1e6 {return nil, false // avoid excessively large exponents}:= .b.abs.expNN(natFive, nat(nil).setWord(Word()), nil, false) // use underlying array of z.b.absif > 0 {.a.abs = .a.abs.mul(.a.abs, ).b.abs = .b.abs.setWord(1)} else {.b.abs =}} else {.b.abs = .b.abs.setWord(1)}// apply exp2 contributionsif < -1e7 || > 1e7 {return nil, false // avoid excessively large exponents}if > 0 {.a.abs = .a.abs.shl(.a.abs, uint())} else if < 0 {.b.abs = .b.abs.shl(.b.abs, uint(-))}.a.neg = && len(.a.abs) > 0 // 0 has no signreturn .norm(), true}// scanExponent scans the longest possible prefix of r representing a base 10// (“e”, “E”) or a base 2 (“p”, “P”) exponent, if any. It returns the// exponent, the exponent base (10 or 2), or a read or syntax error, if any.//// If sepOk is set, an underscore character “_” may appear between successive// exponent digits; such underscores do not change the value of the exponent.// Incorrect placement of underscores is reported as an error if there are no// other errors. If sepOk is not set, underscores are not recognized and thus// terminate scanning like any other character that is not a valid digit.//// exponent = ( "e" | "E" | "p" | "P" ) [ sign ] digits .// sign = "+" | "-" .// digits = digit { [ '_' ] digit } .// digit = "0" ... "9" .//// A base 2 exponent is only permitted if base2ok is set.func ( io.ByteScanner, , bool) ( int64, int, error) {// one char look-ahead, := .ReadByte()if != nil {if == io.EOF {= nil}return 0, 10,}// exponent charswitch {case 'e', 'E':= 10case 'p', 'P':if {= 2break // ok}fallthrough // binary exponent not permitteddefault:.UnreadByte() // ch does not belong to exponent anymorereturn 0, 10, nil}// signvar []byte, = .ReadByte()if == nil && ( == '+' || == '-') {if == '-' {= append(, '-')}, = .ReadByte()}// prev encodes the previously seen char: it is one// of '_', '0' (a digit), or '.' (anything else). A// valid separator '_' may only occur after a digit.:= '.':= false// exponent value:= falsefor == nil {if '0' <= && <= '9' {= append(, )= '0'= true} else if == '_' && {if != '0' {= true}= '_'} else {.UnreadByte() // ch does not belong to number anymorebreak}, = .ReadByte()}if == io.EOF {= nil}if == nil && ! {= errNoDigits}if == nil {, = strconv.ParseInt(string(), 10, 64)}// other errors take precedence over invalid separatorsif == nil && ( || == '_') {= errInvalSep}return}// String returns a string representation of x in the form "a/b" (even if b == 1).func ( *Rat) () string {return string(.marshal())}// marshal implements String returning a slice of bytesfunc ( *Rat) () []byte {var []byte= .a.Append(, 10)= append(, '/')if len(.b.abs) != 0 {= .b.Append(, 10)} else {= append(, '1')}return}// RatString returns a string representation of x in the form "a/b" if b != 1,// and in the form "a" if b == 1.func ( *Rat) () string {if .IsInt() {return .a.String()}return .String()}// FloatString returns a string representation of x in decimal form with prec// digits of precision after the radix point. The last digit is rounded to// nearest, with halves rounded away from zero.func ( *Rat) ( int) string {var []byteif .IsInt() {= .a.Append(, 10)if > 0 {= append(, '.')for := ; > 0; -- {= append(, '0')}}return string()}// x.b.abs != 0, := nat(nil).div(nat(nil), .a.abs, .b.abs):= natOneif > 0 {= nat(nil).expNN(natTen, nat(nil).setUint64(uint64()), nil, false)}= .mul(, ), := .div(nat(nil), , .b.abs)// see if we need to round up= .add(, )if .b.abs.cmp() <= 0 {= .add(, natOne)if .cmp() >= 0 {= nat(nil).add(, natOne)= nat(nil).sub(, )}}if .a.neg {= append(, '-')}= append(, .utoa(10)...) // itoa ignores sign if q == 0if > 0 {= append(, '.'):= .utoa(10)for := - len(); > 0; -- {= append(, '0')}= append(, ...)}return string()}// Note: FloatPrec (below) is in this file rather than rat.go because// its results are relevant for decimal representation/printing.// FloatPrec returns the number n of non-repeating digits immediately// following the decimal point of the decimal representation of x.// The boolean result indicates whether a decimal representation of x// with that many fractional digits is exact or rounded.//// Examples://// x n exact decimal representation n fractional digits// 0 0 true 0// 1 0 true 1// 1/2 1 true 0.5// 1/3 0 false 0 (0.333... rounded)// 1/4 2 true 0.25// 1/6 1 false 0.2 (0.166... rounded)func ( *Rat) () ( int, bool) {// Determine q and largest p2, p5 such that d = q·2^p2·5^p5.// The results n, exact are://// n = max(p2, p5)// exact = q == 1//// For details see:// https://en.wikipedia.org/wiki/Repeating_decimal#Reciprocals_of_integers_not_coprime_to_10:= .Denom().abs // d >= 1// Determine p2 by counting factors of 2.// p2 corresponds to the trailing zero bits in d.// Do this first to reduce q as much as possible.var nat:= .trailingZeroBits()= .shr(, )// Determine p5 by counting factors of 5.// Build a table starting with an initial power of 5,// and use repeated squaring until the factor doesn't// divide q anymore. Then use the table to determine// the power of 5 in q.const = 13 // f == 5^fpvar []nat // tab[i] == (5^fp)^(2^i) == 5^(fp·2^i):= nat{1220703125} // == 5^fp (must fit into a uint32 Word)var , nat // temporariesfor {if _, = .div(, , ); len() != 0 {break // f doesn't divide q evenly}= append(, )= nat(nil).sqr() // nat(nil) to ensure a new f for each table entry}// Factor q using the table entries, if any.// We start with the largest factor f = tab[len(tab)-1]// that evenly divides q. It does so at most once because// otherwise f·f would also divide q. That can't be true// because f·f is the next higher table entry, contradicting// how f was chosen in the first place.// The same reasoning applies to the subsequent factors.var uintfor := len() - 1; >= 0; -- {if , = .div(, , []); len() == 0 {+= * (1 << ) // tab[i] == 5^(fp·2^i)= .set()}}// If fp != 1, we may still have multiples of 5 left.for {if , = .div(, , natFive); len() != 0 {break}++= .set()}return int(max(, )), .cmp(natOne) == 0}
![]() |
The pages are generated with Golds v0.7.6. (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. |