// Copyright 2010 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 multi-precision rational numbers.package bigimport ()// A Rat represents a quotient a/b of arbitrary precision.// The zero value for a Rat represents the value 0.//// Operations always take pointer arguments (*Rat) rather// than Rat values, and each unique Rat value requires// its own unique *Rat pointer. To "copy" a Rat value,// an existing (or newly allocated) Rat must be set to// a new value using the [Rat.Set] method; shallow copies// of Rats are not supported and may lead to errors.typeRatstruct {// To make zero values for Rat work w/o initialization, // a zero value of b (len(b) == 0) acts like b == 1. At // the earliest opportunity (when an assignment to the Rat // is made), such uninitialized denominators are set to 1. // a.neg determines the sign of the Rat, b.neg is ignored. a, b Int}// NewRat creates a new [Rat] with numerator a and denominator b.func (, int64) *Rat {returnnew(Rat).SetFrac64(, )}// SetFloat64 sets z to exactly f and returns z.// If f is not finite, SetFloat returns nil.func ( *Rat) ( float64) *Rat {const = 1<<11 - 1 := math.Float64bits() := & (1<<52 - 1) := int(( >> 52) & )switch {case : // non-finitereturnnilcase0: // denormal -= 1022default: // normal |= 1 << 52 -= 1023 } := 52 - // Optimization (?): partially pre-normalise.for &1 == 0 && > 0 { >>= 1 -- } .a.SetUint64() .a.neg = < 0 .b.Set(intOne)if > 0 { .b.Lsh(&.b, uint()) } else { .a.Lsh(&.a, uint(-)) }return .norm()}// quotToFloat32 returns the non-negative float32 value// nearest to the quotient a/b, using round-to-even in// halfway cases. It does not mutate its arguments.// Preconditions: b is non-zero; a and b have no common factors.func quotToFloat32(, nat) ( float32, bool) {const (// float size in bits = 32// mantissa = 23 = + 1// incl. implicit 1 = + 1// exponent = - = 1<<(-1) - 1 = 1 - = )// TODO(adonovan): specialize common degenerate cases: 1.0, integers. := .bitLen()if == 0 {return0, true } := .bitLen()if == 0 {panic("division by zero") }// 1. Left-shift A or B such that quotient A/B is in [1<<Msize1, 1<<(Msize2+1) // (Msize2 bits if A < B when they are left-aligned, Msize2+1 bits if A >= B). // This is 2 or 3 more than the float32 mantissa field width of Msize: // - the optional extra bit is shifted away in step 3 below. // - the high-order 1 is omitted in "normal" representation; // - the low-order 1 will be used during rounding then discarded. := - var , nat = .set() = .set()if := - ; > 0 { = .shl(, uint()) } elseif < 0 { = .shl(, uint(-)) }// 2. Compute quotient and remainder (q, r). NB: due to the // extra shift, the low-order bit of q is logically the // high-order bit of r.varnat , := .div(, , ) // (recycle a2) := low32() := len() > 0// mantissa&1 && !haveRem => remainder is exactly half// 3. If quotient didn't fit in Msize2 bits, redo division by b2<<1 // (in effect---we accomplish this incrementally).if >> == 1 {if &1 == 1 { = true } >>= 1 ++ }if >> != 1 {panic(fmt.Sprintf("expected exactly %d bits of result", )) }// 4. Rounding.if - <= && <= {// Denormal case; lose 'shift' bits of precision. := uint( - ( - 1)) // [1..Esize1) := & (1<< - 1) = || != 0 >>= = 2 - // == exp + shift }// Round q using round-half-to-even. = !if &1 != 0 { = falseif || &2 != 0 {if ++; >= 1<< {// Complete rollover 11...1 => 100...0, so shift is safe >>= 1 ++ } } } >>= 1// discard rounding bit. Mantissa now scaled by 1<<Msize1. = float32(math.Ldexp(float64(), -))ifmath.IsInf(float64(), 0) { = false }return}// quotToFloat64 returns the non-negative float64 value// nearest to the quotient a/b, using round-to-even in// halfway cases. It does not mutate its arguments.// Preconditions: b is non-zero; a and b have no common factors.func quotToFloat64(, nat) ( float64, bool) {const (// float size in bits = 64// mantissa = 52 = + 1// incl. implicit 1 = + 1// exponent = - = 1<<(-1) - 1 = 1 - = )// TODO(adonovan): specialize common degenerate cases: 1.0, integers. := .bitLen()if == 0 {return0, true } := .bitLen()if == 0 {panic("division by zero") }// 1. Left-shift A or B such that quotient A/B is in [1<<Msize1, 1<<(Msize2+1) // (Msize2 bits if A < B when they are left-aligned, Msize2+1 bits if A >= B). // This is 2 or 3 more than the float64 mantissa field width of Msize: // - the optional extra bit is shifted away in step 3 below. // - the high-order 1 is omitted in "normal" representation; // - the low-order 1 will be used during rounding then discarded. := - var , nat = .set() = .set()if := - ; > 0 { = .shl(, uint()) } elseif < 0 { = .shl(, uint(-)) }// 2. Compute quotient and remainder (q, r). NB: due to the // extra shift, the low-order bit of q is logically the // high-order bit of r.varnat , := .div(, , ) // (recycle a2) := low64() := len() > 0// mantissa&1 && !haveRem => remainder is exactly half// 3. If quotient didn't fit in Msize2 bits, redo division by b2<<1 // (in effect---we accomplish this incrementally).if >> == 1 {if &1 == 1 { = true } >>= 1 ++ }if >> != 1 {panic(fmt.Sprintf("expected exactly %d bits of result", )) }// 4. Rounding.if - <= && <= {// Denormal case; lose 'shift' bits of precision. := uint( - ( - 1)) // [1..Esize1) := & (1<< - 1) = || != 0 >>= = 2 - // == exp + shift }// Round q using round-half-to-even. = !if &1 != 0 { = falseif || &2 != 0 {if ++; >= 1<< {// Complete rollover 11...1 => 100...0, so shift is safe >>= 1 ++ } } } >>= 1// discard rounding bit. Mantissa now scaled by 1<<Msize1. = math.Ldexp(float64(), -)ifmath.IsInf(, 0) { = false }return}// Float32 returns the nearest float32 value for x and a bool indicating// whether f represents x exactly. If the magnitude of x is too large to// be represented by a float32, f is an infinity and exact is false.// The sign of f always matches the sign of x, even if f == 0.func ( *Rat) () ( float32, bool) { := .b.absiflen() == 0 { = natOne } , = quotToFloat32(.a.abs, )if .a.neg { = - }return}// Float64 returns the nearest float64 value for x and a bool indicating// whether f represents x exactly. If the magnitude of x is too large to// be represented by a float64, f is an infinity and exact is false.// The sign of f always matches the sign of x, even if f == 0.func ( *Rat) () ( float64, bool) { := .b.absiflen() == 0 { = natOne } , = quotToFloat64(.a.abs, )if .a.neg { = - }return}// SetFrac sets z to a/b and returns z.// If b == 0, SetFrac panics.func ( *Rat) (, *Int) *Rat { .a.neg = .neg != .neg := .absiflen() == 0 {panic("division by zero") }if &.a == || alias(.a.abs, ) { = nat(nil).set() // make a copy } .a.abs = .a.abs.set(.abs) .b.abs = .b.abs.set()return .norm()}// SetFrac64 sets z to a/b and returns z.// If b == 0, SetFrac64 panics.func ( *Rat) (, int64) *Rat {if == 0 {panic("division by zero") } .a.SetInt64()if < 0 { = - .a.neg = !.a.neg } .b.abs = .b.abs.setUint64(uint64())return .norm()}// SetInt sets z to x (by making a copy of x) and returns z.func ( *Rat) ( *Int) *Rat { .a.Set() .b.abs = .b.abs.setWord(1)return}// SetInt64 sets z to x and returns z.func ( *Rat) ( int64) *Rat { .a.SetInt64() .b.abs = .b.abs.setWord(1)return}// SetUint64 sets z to x and returns z.func ( *Rat) ( uint64) *Rat { .a.SetUint64() .b.abs = .b.abs.setWord(1)return}// Set sets z to x (by making a copy of x) and returns z.func ( *Rat) ( *Rat) *Rat {if != { .a.Set(&.a) .b.Set(&.b) }iflen(.b.abs) == 0 { .b.abs = .b.abs.setWord(1) }return}// Abs sets z to |x| (the absolute value of x) and returns z.func ( *Rat) ( *Rat) *Rat { .Set() .a.neg = falsereturn}// Neg sets z to -x and returns z.func ( *Rat) ( *Rat) *Rat { .Set() .a.neg = len(.a.abs) > 0 && !.a.neg// 0 has no signreturn}// Inv sets z to 1/x and returns z.// If x == 0, Inv panics.func ( *Rat) ( *Rat) *Rat {iflen(.a.abs) == 0 {panic("division by zero") } .Set() .a.abs, .b.abs = .b.abs, .a.absreturn}// Sign returns://// -1 if x < 0// 0 if x == 0// +1 if x > 0func ( *Rat) () int {return .a.Sign()}// IsInt reports whether the denominator of x is 1.func ( *Rat) () bool {returnlen(.b.abs) == 0 || .b.abs.cmp(natOne) == 0}// Num returns the numerator of x; it may be <= 0.// The result is a reference to x's numerator; it// may change if a new value is assigned to x, and vice versa.// The sign of the numerator corresponds to the sign of x.func ( *Rat) () *Int {return &.a}// Denom returns the denominator of x; it is always > 0.// The result is a reference to x's denominator, unless// x is an uninitialized (zero value) [Rat], in which case// the result is a new [Int] of value 1. (To initialize x,// any operation that sets x will do, including x.Set(x).)// If the result is a reference to x's denominator it// may change if a new value is assigned to x, and vice versa.func ( *Rat) () *Int {// Note that x.b.neg is guaranteed false.iflen(.b.abs) == 0 {// Note: If this proves problematic, we could // panic instead and require the Rat to // be explicitly initialized.return &Int{abs: nat{1}} }return &.b}func ( *Rat) () *Rat {switch {caselen(.a.abs) == 0:// z == 0; normalize sign and denominator .a.neg = falsefallthroughcaselen(.b.abs) == 0:// z is integer; normalize denominator .b.abs = .b.abs.setWord(1)default:// z is fraction; normalize numerator and denominator := .a.neg .a.neg = false .b.neg = falseif := NewInt(0).lehmerGCD(nil, nil, &.a, &.b); .Cmp(intOne) != 0 { .a.abs, _ = .a.abs.div(nil, .a.abs, .abs) .b.abs, _ = .b.abs.div(nil, .b.abs, .abs) } .a.neg = }return}// mulDenom sets z to the denominator product x*y (by taking into// account that 0 values for x or y must be interpreted as 1) and// returns z.func mulDenom(, , nat) nat {switch {caselen() == 0 && len() == 0:return .setWord(1)caselen() == 0:return .set()caselen() == 0:return .set() }return .mul(, )}// scaleDenom sets z to the product x*f.// If f == 0 (zero value of denominator), z is set to (a copy of) x.func ( *Int) ( *Int, nat) {iflen() == 0 { .Set()return } .abs = .abs.mul(.abs, ) .neg = .neg}// Cmp compares x and y and returns://// -1 if x < y// 0 if x == y// +1 if x > yfunc ( *Rat) ( *Rat) int {var , Int .scaleDenom(&.a, .b.abs) .scaleDenom(&.a, .b.abs)return .Cmp(&)}// Add sets z to the sum x+y and returns z.func ( *Rat) (, *Rat) *Rat {var , Int .scaleDenom(&.a, .b.abs) .scaleDenom(&.a, .b.abs) .a.Add(&, &) .b.abs = mulDenom(.b.abs, .b.abs, .b.abs)return .norm()}// Sub sets z to the difference x-y and returns z.func ( *Rat) (, *Rat) *Rat {var , Int .scaleDenom(&.a, .b.abs) .scaleDenom(&.a, .b.abs) .a.Sub(&, &) .b.abs = mulDenom(.b.abs, .b.abs, .b.abs)return .norm()}// Mul sets z to the product x*y and returns z.func ( *Rat) (, *Rat) *Rat {if == {// a squared Rat is positive and can't be reduced (no need to call norm()) .a.neg = false .a.abs = .a.abs.sqr(.a.abs)iflen(.b.abs) == 0 { .b.abs = .b.abs.setWord(1) } else { .b.abs = .b.abs.sqr(.b.abs) }return } .a.Mul(&.a, &.a) .b.abs = mulDenom(.b.abs, .b.abs, .b.abs)return .norm()}// Quo sets z to the quotient x/y and returns z.// If y == 0, Quo panics.func ( *Rat) (, *Rat) *Rat {iflen(.a.abs) == 0 {panic("division by zero") }var , Int .scaleDenom(&.a, .b.abs) .scaleDenom(&.a, .b.abs) .a.abs = .abs .b.abs = .abs .a.neg = .neg != .negreturn .norm()}
The pages are generated with Goldsv0.7.0-preview. (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.