767 lines
15 KiB
Go
767 lines
15 KiB
Go
package number
|
||
|
||
import (
|
||
"fmt"
|
||
"math/big"
|
||
"strings"
|
||
|
||
"gitea.zaclys.com/bvaudour/gob/option"
|
||
)
|
||
|
||
// NumberType représente le type d’un nombre.
|
||
type NumberType uint
|
||
|
||
const (
|
||
Integer NumberType = iota
|
||
Scientific
|
||
Decimal
|
||
Fraction
|
||
)
|
||
|
||
// Number représente un nombre.
|
||
type Number struct {
|
||
number rat
|
||
sign int
|
||
tpe NumberType
|
||
base uint
|
||
}
|
||
|
||
func (n *Number) get() (r *big.Rat, ok bool) {
|
||
return n.number.Get()
|
||
}
|
||
|
||
func (n *Number) rat() (r *big.Rat, ok bool) {
|
||
if r, ok = n.get(); ok {
|
||
r = new(big.Rat).Set(r)
|
||
if n.Sign() < 0 {
|
||
r.Neg(r)
|
||
}
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
func (n *Number) format() Number {
|
||
n.base, n.sign, n.tpe = formatBase(n.base), signOf(n.sign), min(n.tpe, Fraction)
|
||
|
||
if nb, ok := n.get(); ok {
|
||
n.sign *= nb.Sign()
|
||
if n.tpe == Integer && !nb.IsInt() {
|
||
num, denom := nb.Num(), nb.Denom()
|
||
nb.SetInt(num.Quo(num, denom))
|
||
}
|
||
nb.Abs(nb)
|
||
} else {
|
||
n.tpe = Integer
|
||
}
|
||
|
||
return *n
|
||
}
|
||
|
||
func (n Number) set(r *big.Rat, tpe NumberType, sign ...int) Number {
|
||
s := 1
|
||
if len(sign) > 0 {
|
||
s = sign[0]
|
||
}
|
||
out := Number{
|
||
base: n.Base(),
|
||
tpe: tpe,
|
||
sign: s,
|
||
number: option.Some(r),
|
||
}
|
||
|
||
return out.format()
|
||
}
|
||
|
||
// Clone crée une copie profonde du nombre.
|
||
func (n Number) Clone() Number {
|
||
out := Number{
|
||
sign: n.sign,
|
||
tpe: n.tpe,
|
||
base: n.base,
|
||
}
|
||
|
||
if nb, ok := n.get(); ok {
|
||
out.number = option.Some(new(big.Rat).Set(nb))
|
||
}
|
||
|
||
return out
|
||
}
|
||
|
||
// Base retourne la base utilisée pour l’affichage du nombre.
|
||
func (n Number) Base() uint { return n.base }
|
||
|
||
// Type retourne le type de nombre (entier, décimal, fraction ou scientifique).
|
||
func (n Number) Type() NumberType { return n.tpe }
|
||
|
||
// Sign retourne :
|
||
// - 0 si n = 0 ou NaN
|
||
// - 1 si n > 0
|
||
// - -1 si n < 0
|
||
func (n Number) Sign() int { return n.sign }
|
||
|
||
// ToBase convertit le nombre dans la base donnée.
|
||
func (n Number) ToBase(base uint) Number {
|
||
out := n.Clone()
|
||
out.base = formatBase(base)
|
||
|
||
return out
|
||
}
|
||
|
||
// ToType convertit le nombre dans le type donné. La base est conservée, sauf si une base est fournie.
|
||
func (n Number) ToType(tpe NumberType, base ...uint) Number {
|
||
out := n.Clone()
|
||
out.tpe = tpe
|
||
out = out.format()
|
||
|
||
if len(base) > 0 {
|
||
return out.ToBase(base[0])
|
||
}
|
||
return out
|
||
}
|
||
|
||
// IsDefined retourne vrai si le nombre est défini.
|
||
func (n Number) IsDefined() bool { return n.number.IsDefined() }
|
||
|
||
// IsInt retourne vrai si le nombre est entier.
|
||
func (n Number) IsInt() bool {
|
||
nb, ok := n.get()
|
||
|
||
return ok && nb.IsInt()
|
||
}
|
||
|
||
// IsInt64 retourne vrai si le nombre est convertible en int64.
|
||
func (n Number) IsInt64() bool {
|
||
nb, ok := n.get()
|
||
|
||
return ok && nb.IsInt() && nb.Num().IsInt64()
|
||
}
|
||
|
||
// IsUint64 retourne vrai si le nombre est convertible en uint64.
|
||
func (n Number) IsUint64() bool {
|
||
nb, ok := n.get()
|
||
|
||
return ok && nb.IsInt() && nb.Num().IsUint64()
|
||
}
|
||
|
||
// Is retourne vrai si n = i.
|
||
func (n Number) Is(i int64) bool {
|
||
if nb, ok := n.rat(); ok && nb.IsInt() {
|
||
num := nb.Num()
|
||
return num.IsInt64() && num.Int64() == i
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
// IsUint retourne vrai si n = u.
|
||
func (n Number) IsUint(u uint64) bool {
|
||
if nb, ok := n.rat(); ok && nb.IsInt() {
|
||
num := nb.Num()
|
||
return num.IsUint64() && num.Uint64() == u
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
// IsFloat retourne vrai si n = f.
|
||
func (n Number) IsFloat(f float64) bool {
|
||
if nb, ok := n.rat(); ok {
|
||
nf, ok := nb.Float64()
|
||
return nf == f && ok
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
// IsNeg retourne vrai si n < 0.
|
||
func (n Number) IsNeg() bool { return n.Sign() < 0 }
|
||
|
||
// IsPos retourne vrai si n > 0.
|
||
func (n Number) IsPos() bool { return !n.IsNeg() }
|
||
|
||
// IsZero retourne vrai si n = 0.
|
||
func (n Number) IsZero() bool { return n.IsDefined() && n.Sign() == 0 }
|
||
|
||
// IsInf retourne vrai si n = +∞.
|
||
func (n Number) IsInf() bool { return !n.IsDefined() && n.Sign() > 0 }
|
||
|
||
// IsNegInf retourne vrai si n = -∞.
|
||
func (n Number) IsNegInf() bool { return !n.IsDefined() && n.Sign() < 0 }
|
||
|
||
// IsNan retourne vrai si n = NaN.
|
||
func (n Number) IsNan() bool { return !n.IsDefined() && n.Sign() == 0 }
|
||
|
||
// ToInt64 retourne le nombre converti en int64, et vrai si la conversion a réussi.
|
||
func (n Number) ToInt64() (i int64, ok bool) {
|
||
var nb *big.Rat
|
||
if nb, ok = n.rat(); ok {
|
||
if ok = nb.IsInt(); ok {
|
||
num := nb.Num()
|
||
if ok = num.IsInt64(); ok {
|
||
i = num.Int64()
|
||
}
|
||
}
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
// ToUint64 retourne le nombre converti en uint64, et vrai si la conversion a réussi.
|
||
func (n Number) ToUint64() (u uint64, ok bool) {
|
||
var nb *big.Rat
|
||
if nb, ok = n.rat(); ok {
|
||
if ok = nb.IsInt(); ok {
|
||
num := nb.Num()
|
||
if ok = num.IsUint64(); ok {
|
||
u = num.Uint64()
|
||
}
|
||
}
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
// ToFloat retourne le nombre converti en flottant, et vrai si la conversion a réussi et est exacte.
|
||
func (n Number) ToFloat() (f float64, ok bool) {
|
||
var nb *big.Rat
|
||
if nb, ok = n.rat(); ok {
|
||
f, ok = nb.Float64()
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
// IsEven retourne vrai si n est entier et pair.
|
||
func (n Number) IsEven() bool {
|
||
if nb, ok := n.get(); ok {
|
||
return nb.IsInt() && nb.Num().Bit(0) == 0
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
// IsOdd retourne vrai si n est entier et impair.
|
||
func (n Number) IsOdd() bool {
|
||
if nb, ok := n.get(); ok {
|
||
return nb.IsInt() && nb.Num().Bit(0) != 0
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
// Num retourne le numérateur.
|
||
func (n Number) Num() Number {
|
||
if n.Sign() == 0 {
|
||
return IntOf(0, n.Base())
|
||
} else if nb, ok := n.rat(); ok {
|
||
return IntOf0(nb.Num(), n.Base())
|
||
}
|
||
|
||
return IntOf(n.Sign(), n.Base())
|
||
}
|
||
|
||
// Denom retourne le dénominateur.
|
||
func (n Number) Denom() Number {
|
||
if nb, ok := n.get(); ok {
|
||
return IntOf0(nb.Denom(), n.Base())
|
||
}
|
||
|
||
return IntOf(0, n.Base())
|
||
}
|
||
|
||
// NumDenom retourne le numérateur et le dénominateur.
|
||
func (n Number) NumDenom() (Number, Number) { return n.Num(), n.Denom() }
|
||
|
||
// Neg retourne -n.
|
||
func (n Number) Neg() Number {
|
||
out := n.Clone()
|
||
out.sign = -out.sign
|
||
|
||
return out
|
||
}
|
||
|
||
// Abs retourne |n|.
|
||
func (n Number) Abs() Number {
|
||
out := n.Clone()
|
||
out.sign = abs(out.sign)
|
||
|
||
return out
|
||
}
|
||
|
||
// Cmp retourne :
|
||
// - 1 si n1 > n2
|
||
// - -1 si n1 < n2
|
||
// - 0 si n1 = n2
|
||
// - 2 ou -2 si les nombres ne sont pas comparables (ie. n1 = NaN ou n2 = NaN)
|
||
func (n1 Number) Cmp(n2 Number) int {
|
||
if n1.IsNan() {
|
||
if n2.IsNan() {
|
||
return 0
|
||
}
|
||
return -2
|
||
} else if n2.IsNan() {
|
||
return 2
|
||
} else if nb1, ok := n1.rat(); ok {
|
||
if nb2, ok := n2.rat(); ok {
|
||
return nb1.Cmp(nb2)
|
||
}
|
||
return -n2.Sign()
|
||
} else if n2.IsDefined() {
|
||
return n1.Sign()
|
||
}
|
||
|
||
return compare(n1.Sign(), n2.Sign())
|
||
}
|
||
|
||
// Eq retourne vrai si n1 = n2.
|
||
func (n1 Number) Eq(n2 Number) bool { return n1.Cmp(n2) == 0 }
|
||
|
||
// Ne retourne vrai si n1 ≠ n2.
|
||
func (n1 Number) Ne(n2 Number) bool { return n1.Cmp(n2) != 0 }
|
||
|
||
// Gt retourne vrai si n1 > n2.
|
||
func (n1 Number) Gt(n2 Number) bool { return n1.Cmp(n2) > 0 }
|
||
|
||
// Lt retourne vrai si n1 < n2.
|
||
func (n1 Number) Lt(n2 Number) bool { return n1.Cmp(n2) < 0 }
|
||
|
||
// Ge retourne vrai si n1 ≥ n2.
|
||
func (n1 Number) Ge(n2 Number) bool { return n1.Cmp(n2) >= 0 }
|
||
|
||
// Le retourne vrai si n1 ≤ n2.
|
||
func (n1 Number) Le(n2 Number) bool { return n1.Cmp(n2) <= 0 }
|
||
|
||
// Add retourne n1 + n2.
|
||
func (n1 Number) Add(n2 Number) Number {
|
||
if r1, ok := n1.rat(); ok {
|
||
if r2, ok := n2.rat(); ok {
|
||
return n1.set(new(big.Rat).Add(r1, r2), max(n1.Type(), n2.Type()))
|
||
}
|
||
return n2.Clone()
|
||
} else if n2.IsDefined() || n1.Sign() == n2.Sign() {
|
||
return Undefined(n1.Sign())
|
||
}
|
||
|
||
return Nan()
|
||
}
|
||
|
||
// Sub retourne n1 − n2.
|
||
func (n1 Number) Sub(n2 Number) Number { return n1.Add(n2.Neg()) }
|
||
|
||
// Inc retourne n + 1.
|
||
func (n Number) Inc() Number { return n.Add(One()) }
|
||
|
||
// Dec retourne n − 1.
|
||
func (n Number) Dec() Number { return n.Sub(One()) }
|
||
|
||
// Mul retourne n1 × n2.
|
||
func (n1 Number) Mul(n2 Number) Number {
|
||
s := n1.Sign() * n2.Sign()
|
||
if nb1, ok := n1.get(); ok {
|
||
if nb2, ok := n2.get(); ok {
|
||
return n1.set(new(big.Rat).Mul(nb1, nb2), max(n1.Type(), n2.Type()), s)
|
||
}
|
||
}
|
||
|
||
return Undefined(s)
|
||
}
|
||
|
||
// Div retourne n1 / n2 (division exacte).
|
||
func (n1 Number) Div(n2 Number) Number {
|
||
s := n1.Sign() * n2.Sign()
|
||
if nb1, ok := n1.get(); ok {
|
||
if nb2, ok := n2.get(); ok {
|
||
if nb2.IsInt() && nb2.Num().IsInt64() && nb2.Num().Int64() == 0 {
|
||
return Nan()
|
||
}
|
||
|
||
t := max(n1.Type(), n2.Type())
|
||
result := new(big.Rat).Quo(nb1, nb2)
|
||
if t == Integer && !result.IsInt() {
|
||
t = Decimal
|
||
}
|
||
|
||
return n1.set(result, t, s)
|
||
}
|
||
|
||
return Undefined(s)
|
||
} else if n2.IsDefined() {
|
||
return Undefined(s)
|
||
}
|
||
|
||
return Nan()
|
||
}
|
||
|
||
// inv retourne 1/n.
|
||
func (n Number) Inv() Number { return IntOf(1, n.Base()).Div(n) }
|
||
|
||
// Quo retourne n1 ÷ n2 (division entière).
|
||
func (n1 Number) Quo(n2 Number) Number {
|
||
if nb1, ok := n1.rat(); ok {
|
||
if nb2, ok := n2.rat(); ok {
|
||
if nb2.IsInt() && nb2.Num().IsInt64() && nb2.Num().Int64() == 0 {
|
||
return Nan()
|
||
}
|
||
|
||
q := new(big.Rat).Quo(nb1, nb2)
|
||
return IntOf0(new(big.Int).Quo(q.Num(), q.Denom()), n1.Base())
|
||
}
|
||
|
||
return Undefined(n1.Sign() * n2.Sign())
|
||
} else if n2.IsDefined() {
|
||
return Undefined(n1.Sign() * n2.Sign())
|
||
}
|
||
|
||
return Nan()
|
||
}
|
||
|
||
// QuoRem retourne q et r tels que n1 = q×n2 + r où q est entier.
|
||
func (n1 Number) QuoRem(n2 Number) (q, r Number) {
|
||
q = n1.Quo(n2)
|
||
r = n1.Sub(n2.Mul(q))
|
||
|
||
return
|
||
}
|
||
|
||
// Rem retourne n1 % n2.
|
||
func (n1 Number) Rem(n2 Number) Number {
|
||
_, r := n1.QuoRem(n2)
|
||
|
||
return r
|
||
}
|
||
|
||
func (n Number) pow(p int64) (out Number) {
|
||
inv := p < 0
|
||
p = abs(p)
|
||
|
||
switch {
|
||
case p == 0:
|
||
if n.IsNan() {
|
||
return Nan()
|
||
}
|
||
return IntOf(1, n.Base())
|
||
case p == 1:
|
||
out = n.Clone()
|
||
case p == 2:
|
||
out = n.Square()
|
||
case p&1 == 0:
|
||
out = n.Square().pow(p >> 1)
|
||
default:
|
||
out = n.Square().pow(p >> 1).Mul(n)
|
||
}
|
||
|
||
if inv {
|
||
out = out.Inv()
|
||
}
|
||
return
|
||
}
|
||
func (n Number) optimize(precision Number) (out Number) {
|
||
out = n
|
||
if n.Denom().Gt(precision) {
|
||
out = n.Num().Mul(precision).Quo(n.Denom()).Div(precision).ToType(n.Type())
|
||
}
|
||
|
||
return
|
||
}
|
||
func (n Number) sqrt(s uint64) (out Number) {
|
||
if n.IsNan() || s < 2 || (s&1 == 0 && n.IsNeg()) {
|
||
return Nan()
|
||
} else if !n.IsDefined() {
|
||
return n.Clone()
|
||
}
|
||
|
||
s0 := IntOf(s)
|
||
s1 := s0.Dec()
|
||
precision := pow[uint, uint](n.Base())
|
||
deltaMax := precision.Inv()
|
||
heron := func(partial Number) Number {
|
||
return ((partial.Mul(s1)).Add(n.Div(partial.pow(int64(s - 1))))).Div(s0).optimize(precision)
|
||
}
|
||
|
||
out = n
|
||
for {
|
||
tmp := heron(out)
|
||
if out.Eq(tmp) {
|
||
break
|
||
} else if (tmp.Sub(out)).Abs().Lt(deltaMax) {
|
||
out = tmp
|
||
break
|
||
}
|
||
out = tmp
|
||
}
|
||
|
||
if n.Type() == Integer && out.IsInt() {
|
||
out = out.ToType(Integer)
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
// Pow retourne n1 ^ n2.
|
||
func (n1 Number) Pow(n2 Number) Number {
|
||
if !n2.IsDefined() {
|
||
return Nan()
|
||
}
|
||
|
||
p2, s2 := n2.NumDenom()
|
||
p, pok := p2.ToInt64()
|
||
s, sok := s2.ToUint64()
|
||
if !pok || !sok {
|
||
return Nan()
|
||
}
|
||
|
||
out := n1.pow(p)
|
||
if s > 1 {
|
||
out = out.sqrt(s)
|
||
}
|
||
|
||
return out
|
||
}
|
||
|
||
// Square retourne n².
|
||
func (n Number) Square() Number { return n.Mul(n) }
|
||
|
||
// Sqrtn retourne la racine n2ième de n1.
|
||
func (n1 Number) Sqrtn(n2 Number) Number {
|
||
s, ok := n2.ToUint64()
|
||
|
||
if !ok {
|
||
return Nan()
|
||
}
|
||
return n1.sqrt(s)
|
||
}
|
||
|
||
// Sqrt retourne la racine carrée de n.
|
||
func (n Number) Sqrt() Number { return n.Sqrtn(Two()) }
|
||
|
||
func (n Number) lsh(s uint64) Number {
|
||
if nb, ok := n.rat(); ok {
|
||
num, denom := nb.Num(), nb.Denom()
|
||
return FracOf0(new(big.Int).Lsh(num, uint(s)), denom, n.Base()).ToType(n.Type())
|
||
}
|
||
|
||
return n.Clone()
|
||
}
|
||
func (n Number) rsh(s uint64) Number {
|
||
if nb, ok := n.rat(); ok {
|
||
t := n.Type()
|
||
if t == Integer {
|
||
return IntOf0(new(big.Int).Rsh(nb.Num(), uint(s)), n.Base())
|
||
}
|
||
return FracOf0(nb.Num(), new(big.Int).Lsh(nb.Denom(), uint(s)), n.Base()).ToType(t)
|
||
}
|
||
|
||
return n.Clone()
|
||
}
|
||
|
||
// Lsh retourne n1 << n2.
|
||
func (n1 Number) Lsh(n2 Number) Number {
|
||
if s, ok := n2.ToUint64(); ok {
|
||
return n1.lsh(s)
|
||
}
|
||
|
||
return Nan()
|
||
}
|
||
|
||
// Rsh retourne n1 >> n2.
|
||
func (n1 Number) Rsh(n2 Number) Number {
|
||
if s, ok := n2.ToUint64(); ok {
|
||
return n1.rsh(s)
|
||
}
|
||
|
||
return Nan()
|
||
}
|
||
|
||
// Len retourne le nombre de chiffre de n si n est entier ou -1 sinon.
|
||
func (n Number) Len() int {
|
||
if nb, ok := n.get(); ok {
|
||
if !nb.IsInt() {
|
||
return -1
|
||
}
|
||
num := nb.Num()
|
||
if num.IsInt64() && num.Int64() == 0 {
|
||
return 1
|
||
}
|
||
s := num.Text(int(n.Base()))
|
||
return len(s)
|
||
}
|
||
|
||
return -1
|
||
}
|
||
|
||
// Bit retourne le n2ième chiffre de n1 si n est entier, ou -1 sinon.
|
||
// Le n° de bit commence à 0 à partir de la droite.
|
||
func (n1 Number) Bit(n2 Number) int {
|
||
b, ok := n2.ToUint64()
|
||
if !ok || !n1.IsInt() {
|
||
return -1
|
||
}
|
||
|
||
out := n1
|
||
if b > 0 {
|
||
out = n1.Quo(pow(n1.Base(), b))
|
||
}
|
||
|
||
out = out.Rem(IntOf(n1.Base()))
|
||
if result, ok := out.ToInt64(); ok {
|
||
return int(result)
|
||
}
|
||
|
||
return -1
|
||
}
|
||
|
||
// Fact retourne n!.
|
||
func (n Number) Fact() Number {
|
||
switch {
|
||
case n.IsInf():
|
||
return Inf()
|
||
case !n.IsInt() || n.IsNeg():
|
||
return Nan()
|
||
default:
|
||
out := One(n.Base())
|
||
for e := Two(); e.Le(n); e = e.Inc() {
|
||
out = out.Mul(e)
|
||
}
|
||
return out
|
||
}
|
||
}
|
||
|
||
func (n Number) text() string {
|
||
if nb, ok := n.get(); ok {
|
||
return nb.Num().Text(int(n.Base()))
|
||
}
|
||
return ""
|
||
}
|
||
func (n Number) undefinedString() string {
|
||
switch n.Sign() {
|
||
case -1:
|
||
return "-∞"
|
||
case +1:
|
||
return "+∞"
|
||
default:
|
||
return "NaN"
|
||
}
|
||
}
|
||
func (n Number) intString() (out string) {
|
||
out = n.text()
|
||
|
||
if n.Sign() < 0 {
|
||
out = fmt.Sprintf("-%s", out)
|
||
}
|
||
|
||
if base := n.Base(); base != 10 {
|
||
out = fmt.Sprintf("(%d)%s", base, out)
|
||
}
|
||
|
||
return
|
||
}
|
||
func (n Number) fracString() string {
|
||
num, denom := n.NumDenom()
|
||
|
||
return fmt.Sprintf("%s/%s", num.intString(), denom.intString())
|
||
}
|
||
func (n Number) decString() (out string) {
|
||
base, sign := n.Base(), n.Sign()
|
||
num, denom := n.NumDenom()
|
||
precision := pow[uint, uint](base)
|
||
|
||
num = num.Abs().Mul(precision)
|
||
q := num.Quo(denom)
|
||
|
||
out = "0"
|
||
if num.Ge(denom) {
|
||
out = q.text()
|
||
}
|
||
|
||
p := int(FloatingPrecision)
|
||
if len(out) < p {
|
||
out = fmt.Sprintf("%s%s", strings.Repeat("0", p), out)
|
||
}
|
||
|
||
dot := len(out) - p
|
||
out = fmt.Sprintf("%s.%s", out[:dot], out[dot:])
|
||
|
||
for out[0] == '0' {
|
||
out = out[1:]
|
||
}
|
||
|
||
if !FixedPrecision {
|
||
l := len(out) - 1
|
||
for out[l] == '0' {
|
||
out, l = out[:l], l-1
|
||
}
|
||
}
|
||
|
||
if out == "." {
|
||
out = "0."
|
||
}
|
||
|
||
if sign < 0 {
|
||
out = fmt.Sprintf("-%s", out)
|
||
} else if q.IsZero() && sign > 0 {
|
||
out = fmt.Sprintf("+%s", out)
|
||
}
|
||
|
||
if base != 10 {
|
||
out = fmt.Sprintf("(%d)%s", base, out)
|
||
}
|
||
|
||
return
|
||
}
|
||
func (n Number) sciString() (out string) {
|
||
base, sign := n.Base(), n.Sign()
|
||
var exponent int
|
||
tmp := n
|
||
if !n.IsZero() {
|
||
num, denom := n.Abs().NumDenom()
|
||
nl, dl := num.Len(), denom.Len()
|
||
exponent = nl - dl
|
||
nBase := IntOf(base)
|
||
if exponent > 0 {
|
||
denom = denom.Mul(nBase.pow(int64(exponent)))
|
||
} else if exponent < 0 {
|
||
num = num.Mul(nBase.pow(int64(-exponent)))
|
||
}
|
||
|
||
if num.Lt(denom) {
|
||
exponent--
|
||
num = num.Mul(nBase)
|
||
}
|
||
tmp = num.Div(denom)
|
||
}
|
||
|
||
if sign < 0 {
|
||
tmp = tmp.Neg()
|
||
}
|
||
|
||
signExponent := ""
|
||
if exponent > 0 {
|
||
signExponent = "+"
|
||
}
|
||
|
||
out = tmp.decString()
|
||
if base == 10 {
|
||
return fmt.Sprintf("%sE%s%d", out, signExponent, exponent)
|
||
}
|
||
return fmt.Sprintf("%s×%d^%s%d", out, base, signExponent, exponent)
|
||
}
|
||
|
||
// String retourne la représentation du nombre sous forme de chaîne de caractères.
|
||
func (n Number) String() string {
|
||
if !n.IsDefined() {
|
||
return n.undefinedString()
|
||
}
|
||
|
||
switch n.Type() {
|
||
case Integer:
|
||
return n.intString()
|
||
case Fraction:
|
||
return n.fracString()
|
||
case Scientific:
|
||
return n.sciString()
|
||
default:
|
||
return n.decString()
|
||
}
|
||
}
|