rational — exact rational number with Result arithmetic
github.com/go-composites/rational (Go package Rational, imported from the
src/ sub-directory) is an exact rational-number composite over Go's stdlib
math/big.Rat (Ruby's Rational as the reference). A fraction is always stored
in lowest terms and is exact — 1/3 + 1/6 is exactly 1/2, with no
floating-point error. Its fallible operations (the constructors and Div)
return a result so that failures — a zero denominator or a
division by zero — are values, never panics or bare nil.
It extends the numeric tower alongside number (Ruby's Integer
and Float) and bignumber (Ruby's unbounded Integer): where
those box whole or floating-point values, rational carries an exact
fraction.
API
import Rational "github.com/go-composites/rational/src"
type Interface interface {
Numerator() int64
Denominator() int64
ToGoString() string
ToFloat() float64
IsNull() bool
Add(Interface) Result.Interface
Sub(Interface) Result.Interface
Mul(Interface) Result.Interface
Div(Interface) Result.Interface
Abs() Result.Interface
Neg() Result.Interface
Equal(Interface) bool
LessThan(Interface) bool
GreaterThan(Interface) bool
Inspect() string
}
func FromInts(num, den int64) Result.Interface
func FromString(s string) Result.Interface
func Null() Interface
FromInts(num, den)builds aRationalfrom two Goint64values, reduced to lowest terms, and returns aResult; on success its payload is theRational. Whendenis0theResultcarries anError.New("zero denominator")instead of a payload — the construction never panics and never returnsnil.FromString(s)parses a fraction string such as"3/4"and returns aResult; on success its payload is the parsed, reducedRational, otherwise it carries anError.New("invalid rational: ..."). The parse never panics and never returnsnil.Null()returns the Null-Object rational (see below).
| Member | Behaviour |
|---|---|
Numerator() |
Returns the numerator of the reduced fraction as a Go int64. |
Denominator() |
Returns the denominator of the reduced fraction as a Go int64. |
ToGoString() |
Returns the "num/den" representation of the reduced fraction. |
ToFloat() |
Returns the value as a Go float64. This conversion is lossy; the exact value is preserved by ToGoString() and the arithmetic methods. |
IsNull() |
Returns false — a concrete Rational is never null. |
Add / Sub / Mul |
Return a Result whose payload is a new Rational carrying the sum / difference / product. A fresh big.Rat backs the payload; operands are never mutated. |
Div(other) |
Returns a Result whose payload is the quotient — unless other is a zero rational, in which case the Result carries an Error.New("division by zero") instead of a payload. It never panics and never returns nil. |
Abs() |
Returns a Result whose payload is a new Rational holding the absolute value. |
Neg() |
Returns a Result whose payload is a new Rational holding the negation. |
Equal / LessThan / GreaterThan |
Return a plain Go bool comparing the rational values. |
Inspect() |
Returns a one-line <Rational:... value=...> string. |
Usage
a := Rational.FromString("1/3").Payload().(Rational.Interface)
b := Rational.FromString("1/6").Payload().(Rational.Interface)
// Exactness — 1/3 + 1/6 is exactly 1/2, with no float error:
if r := a.Add(b); !r.HasError() {
sum := r.Payload().(Rational.Interface)
fmt.Println(sum.ToGoString()) // 1/2
half := Rational.FromInts(1, 2).Payload().(Rational.Interface)
fmt.Println(sum.Equal(half)) // true
}
// A zero denominator is a value, not a panic:
if r := Rational.FromInts(1, 0); r.HasError() {
fmt.Println(r.Error().Message()) // zero denominator
}
// Division by a zero rational is a value, not a panic:
zero := Rational.FromInts(0, 1).Payload().(Rational.Interface)
if r := a.Div(zero); r.HasError() {
fmt.Println(r.Error().Message()) // division by zero
}
The Null-Object variant
Rational.Null() honours the full Interface without ever being nil:
Numerator()/Denominator() are 0, ToGoString() is "", ToFloat() is
0, every arithmetic method returns a Result carrying a
method-not-implemented error, Equal(other) is other.IsNull(),
LessThan/GreaterThan are false, Inspect() is <NullRational>, and
IsNull() returns true.
Dependencies
rational depends on result and error (and
transitively on null).