Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,54 @@ func continuedFraction(n float64, i int64, limit int64, prec float64) *big.Rat {
return res
}

func floatToFraction(x float64, numeratorPlaceHolder, denominatorPlaceHolder int) string {
if denominatorPlaceHolder <= 0 {
return ""
}
var rat string
num, den := floatToFracUseContinuedFraction(x, int64(math.Pow10(denominatorPlaceHolder)))
if num == 0 {
rat = strings.Repeat(" ", numeratorPlaceHolder+denominatorPlaceHolder+1)
} else {
numStr := strconv.FormatInt(num, 10)
denStr := strconv.FormatInt(den, 10)
numeratorPlaceHolder = max(numeratorPlaceHolder-len(numStr), 0)
denominatorPlaceHolder = max(denominatorPlaceHolder-len(denStr), 0)
rat = fmt.Sprintf("%s%s/%s%s", strings.Repeat(" ", numeratorPlaceHolder), numStr, denStr, strings.Repeat(" ", denominatorPlaceHolder))
}
return rat
}

// floatToFracUseContinuedFraction implement convert a floating-point decimal
// to a fraction using continued fractions and recurrence relations.
func floatToFracUseContinuedFraction(x float64, denominatorLimit int64) (num, den int64) {
p_1 := int64(1)
q_1 := int64(0)
p_2 := int64(0)
q_2 := int64(1)
var lasta, lastb int64
var curra, currb int64
for i := 0; i < 100; i++ {
a := int64(math.Floor(x))
curra, currb = a*p_1+p_2, a*q_1+q_2
p_2 = p_1
q_2 = q_1
p_1 = curra
q_1 = currb
frac := x - float64(a)
if q_1 >= denominatorLimit {
return lasta, lastb //big.NewRat(lasta, lastb)
}
if math.Abs(frac) < 1e-12 {
return curra, currb //big.NewRat(curra, currb)
}

lasta, lastb = curra, currb
x = 1.0 / frac
}
return lasta, lastb //big.NewRat(lasta, lastb)
}

// assignFieldValue assigns the value from an immutable reflect.Value to a
// mutable reflect.Value based on the type of the immutable value.
func assignFieldValue(field string, immutable, mutable reflect.Value) {
Expand Down
37 changes: 37 additions & 0 deletions lib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/xml"
"fmt"
"io"
"math"
"os"
"strconv"
"strings"
Expand Down Expand Up @@ -412,3 +413,39 @@ func TestUnzipToTemp(t *testing.T) {
_, err = f.unzipToTemp(z.File[0])
assert.EqualError(t, err, "EOF")
}

func oldFractionHandler(frac float64, fracPlaceHolder int) string {
var rat string
for i := 0; i < 5000; i++ {
if r := newRat(frac, int64(i), 0); len(r.Denom().String()) <= fracPlaceHolder {
if rat = r.String(); strings.HasPrefix(rat, "0/") {
rat = strings.Repeat(" ", 3)
}
continue
}
break
}
return rat
}

func TestFloat2Frac(t *testing.T) {
valueList := []float64{0.19, 0.54, 0.9, 0.99, 0.999, 0.999}
for _, v := range valueList {
for idx := 0; idx <= 10; idx++ {
res1 := strings.Trim(oldFractionHandler(v, idx), " ")
res2 := strings.Trim(floatToFraction(v, idx, idx), " ")
assert.Equal(t, res1, res2, "value %f, fracPlaceHolder %d", v, idx)
}
}
for k := range 10 {
val := math.Pi / math.Pow(10, float64(k))
for idx := 1; idx <= 5; idx++ {
res1 := strings.Trim(oldFractionHandler(val, idx), " ")
res2 := strings.Trim(floatToFraction(val, idx, idx), " ")
assert.Equal(t, res1, res2, "value %f, fracPlaceHolder %d", val, idx)
if res1 != "" {
t.Log(idx, val, res1, res2)
}
}
}
}
26 changes: 4 additions & 22 deletions numfmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -5439,27 +5439,8 @@ func (nf *numberFormat) printNumberLiteral(text string) string {
// negative numeric.
func (nf *numberFormat) fractionHandler(frac float64, token nfp.Token, numeratorPlaceHolder int) string {
var rat, result string
var lastRat *big.Rat
if token.TType == nfp.TokenTypeDigitalPlaceHolder {
denominatorPlaceHolder := len(token.TValue)
for i := range 5000 {
if r := newRat(frac, int64(i), 0); len(r.Denom().String()) <= denominatorPlaceHolder {
lastRat = r // record the last valid ratio, and delay conversion to string
continue
}
break
}
if lastRat != nil {
if lastRat.Num().Int64() == 0 {
rat = strings.Repeat(" ", numeratorPlaceHolder+denominatorPlaceHolder+1)
} else {
num := lastRat.Num().String()
den := lastRat.Denom().String()
numeratorPlaceHolder = max(numeratorPlaceHolder-len(num), 0)
denominatorPlaceHolder = max(denominatorPlaceHolder-len(den), 0)
rat = fmt.Sprintf("%s%s/%s%s", strings.Repeat(" ", numeratorPlaceHolder), num, den, strings.Repeat(" ", denominatorPlaceHolder))
}
}
rat = floatToFraction(frac, numeratorPlaceHolder, len(token.TValue))
result += rat
}
if token.TType == nfp.TokenTypeDenominator {
Expand Down Expand Up @@ -5539,8 +5520,9 @@ func (nf *numberFormat) numberHandler() string {
intLen, fracLen = nf.getNumberPartLen()
result string
)
if isNum, precision, decimal := isNumeric(nf.value); isNum {
if precision > 15 && intLen+fracLen > 15 && !nf.useScientificNotation {
if intLen+fracLen > 15 && !nf.useScientificNotation {
isNum, precision, decimal := isNumeric(nf.value)
if isNum && precision > 15 {
return nf.printNumberLiteral(nf.printBigNumber(decimal, fracLen))
}
}
Expand Down
3 changes: 1 addition & 2 deletions numfmt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4350,8 +4350,7 @@ func BenchmarkNumFmtPlaceHolder(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for _, item := range items {
result := format(item[0], item[1], false, CellTypeNumber, nil)
_ = result
_ = format(item[0], item[1], false, CellTypeNumber, nil)
}
}
}