Commit 5e8eb2c2 authored by Mitar's avatar Mitar
Browse files

Add RatPrecision.

parent 5cfbb5b3
Pipeline #446907185 passed with stages
in 2 minutes and 9 seconds
package x
import (
"math/big"
)
var (
zeroInt = big.NewInt(0)
oneInt = big.NewInt(1)
twoInt = big.NewInt(2) //nolint:gomnd
fiveInt = big.NewInt(5) //nolint:gomnd
tenInt = big.NewInt(10) //nolint:gomnd
)
// RatPrecision computes for rat the number of non-repeating digits on the right
// of the decimal point and the number of repeating digits which cyclicly follow.
//
// It can be used with Rat.FloatString to convert a number to full precision
// representation, when there are no repeating digits.
func RatPrecision(rat *big.Rat) (int, int) {
// Go assures that in is normalized.
m := new(big.Int).Set(rat.Denom())
q := new(big.Int)
r := new(big.Int)
k := 0
for {
q.QuoRem(m, twoInt, r)
if r.Cmp(zeroInt) == 0 {
m, q = q, m
k++
} else {
break
}
}
l := 0
for {
q.QuoRem(m, fiveInt, r)
if r.Cmp(zeroInt) == 0 {
m, q = q, m
l++
} else {
break
}
}
j := 0
if m.Cmp(oneInt) != 0 {
q.SetInt64(1)
for {
q.Mul(q, tenInt)
q.Mod(q, m)
j++
if q.Cmp(oneInt) == 0 {
break
}
}
}
if k > l {
return k, j
}
return l, j
}
package x_test
import (
"fmt"
"math/big"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gitlab.com/tozd/go/x"
)
func TestRatPrecision(t *testing.T) {
tests := []struct {
n int64
m int64
k int
r int
}{
{1, 3, 0, 1},
{1, 6, 1, 1},
{1, 7, 0, 6},
{1, 9, 0, 1},
{1, 28, 2, 6},
{1, 67, 0, 33},
{1, 81, 0, 9},
{1, 96, 5, 1},
{8, 13, 0, 6},
{2, 14, 0, 6},
{3, 30, 1, 0},
{2, 3, 0, 1},
{9, 11, 0, 2},
{7, 12, 2, 1},
{22, 7, 0, 6},
}
for _, test := range tests {
t.Run(fmt.Sprintf("%d/%d", test.n, test.m), func(t *testing.T) {
l, q := x.RatPrecision(big.NewRat(test.n, test.m))
assert.Equal(t, test.k, l)
assert.Equal(t, test.r, q)
})
}
}
func TestRatPrecisionString(t *testing.T) {
tests := []string{
"123.34",
"-2342343.2321234442",
"235994.099923999900001",
}
for _, test := range tests {
t.Run(test, func(t *testing.T) {
n, ok := new(big.Rat).SetString(test)
require.True(t, ok)
l, q := x.RatPrecision(n)
assert.Equal(t, 0, q)
assert.Equal(t, test, n.FloatString(l))
})
}
}
func BenchmarkRatPrecision(b *testing.B) {
r := big.NewRat(1, 67)
for n := 0; n < b.N; n++ {
x.RatPrecision(r)
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment