Parse big.Float and print it back out and get the exact same output as input

Just as an exercise for a project I am working on, I need to be able to parse arbitrary precision numbers and be able to print them back out and get the exact same output string as the input string.

I can not figure out the incantation to do this with big.Float!

I tried using f, i, err := big.ParseFloat(pi, 10, big.MaxPrec, big.AwayFromZero) and I got the exact some wrong output, so I can not tell if the parsing is wrong, the formatting the output is wrong or BOTH?

These functions are not very well documented, as in the i return value is not described what it is supposed to represent anywhere I can find. It has a value of 10 every time, I can only assume it is the base.

package main

import (
    "fmt"
    "math/big"
    "strings"
)

func main() {
    pi := "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153644"
    fmt.Println(pi)
    precision := uint(len(pi[strings.Index(pi,".")+1:]))
    fmt.Printf("%d decimal digits!\n", precision)
    bf := big.NewFloat(0.0)
    bf, _ = bf.SetPrec(precision).SetMode(bf.Mode()).SetString(pi)
    fmt.Println(bf.Text('f',int(precision)))
}

this is the output I am getting

3.141592653589793238462643383279502884197169399375105820974944592307816406286208
99862803482534211706798214808651328230664709384460955058223172535940812848111745
02841027019385211055596446229489549303819644288109756659334461284756482337867831
65271201909145648566923460348610454326648213393607260249141273724587006606315588
1748815209209628292540917153644
349 decimal digits!
3.141592653589793238462643383279502884197169399375105820974944592307816406286208
99862803482534211706798214642892769097375992970630463040280967955002394205104670
93398509790876622872910336294129674153835313200604005620450374388405009045870250
42707837345817603660943583398075476645872182159499972077156507317945849377949230
0745914690196514129638671875000

mantissa precision of x in bits.

Digits in base-10 take more than 1 bit. You are basing precision on the number of digits. At least triple it.

precision x 3 is no where near enough, precision x 7 gets the correct answer sort of.

as you can see, I get the original string PLUS a bunch of rounding garbage at the end.

how would I calculate the correct * bits precision based on the number of desired digits in the output string?

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153644

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153644000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002244342506374987052728132873357764604822964011725723498156139035608263529693321324611087760931638291534783804429504024063970113225287605729963719722383580878304141959206687069400914796848561621434478110671369148893967123772756235371122602256507537069295793796127829048985435018724653366850521683797027866210566225126917930722700540281778806181488530238605442879753445529001873898888929241864013642282373527291515685288403981807650734267848295316694745558591914884141327648624551152976025240472366982876042065424920222257500605683086490572532495407799867513435663181757387051418102492504661099733895817691933392065321975406088794616695914589059426846076572409593363011614759573979870328010001015131814985977529961056716665782401251298757553305676392622078175716128094105318036037245992472681494427947436144326364838683179041263870046413457172155738170850090737732421416031506418182930764950812479381058736513575328259855587826691707281198805818862277495355406830418592809361721477237559115847998907779919675848738643420198693152512349267893520195348481384845351896090256789774024980305634166361874489554298999467170401080881950315354127157986245696801549849750033397305982685708142528398925768672801947396154320512030099540703574095339226368627750879580770901088754012337545509255182543249838242985702416360011638303226834983986987743539557515819170488243938973917949619935857136645165558615729339233345028341636697904845161310408856339696647756621712434792748303616469785554055231767976392050919715124254881461507226059126555805330182014130173637310850569765132473800857139233811910170967773594655986084933999505845270291677052663693276509280026011803914534022466858304012404801142110954970121383666992187500

after some checking it seems that precision == 2400 is the smallest number that I can use that gets me the correct output + the zeros and garbage at the end.

It looks like an exact match when multiplying by 4. Go Playground - The Go Programming Language

A single decimal digit can take values from 0 to 9, so 10 different values.

So you need log2(10) bit to represent a single decimal digit.

Thanks, I was being lazy. With log2(10) you save 236 bits, FWIW

I was doing this;

precision := uint(len(pi[strings.Index(pi, ".")+1:]) * 4)

and that does not work, neither does

precision := uint(len(pi[strings.Index(pi, ".")+1:])) * 4

but this works, this is mine numbing stupid behavior

bf, _ = bf.SetPrec(precision * 4).SetMode(bf.Mode()).SetString(pi)

this is the smallest precision bit number that gives the correct result.

bf, _ = bf.SetPrec(1161).SetMode(bf.Mode()).SetString(pi)

1160 rounds the last digit up, 1159 rounds the last digit down, 1161 is just right.

Thanks for all the replies!

Those extra bits are for the leading 3 in front of the comma!

so given an arbitrary number of digits before the . and after the . how would one calculate the correct number of bits to feed into the .SetPrec() function?

Consider all digits, not only those after the comma.

So here is a complete working solution/example that takes into consideration the integer and fractional digit counts and as far as my limited testing shows, returns the exact same string representation as the input value.

pi := "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446095505822317253594081284811174502841027019385211055596446229489549303819644288109756659334461284756482337867831652712019091456485669234603486104543266482133936072602491412737245870066063155881748815209209628292540917153644"
fmt.Printf("%s\n", pi)
integerDigitCount := strings.Index(pi, ".")
fractionalDigitCount := len(pi) - (integerDigitCount + 1) // exclude the decimal from the count
fmt.Printf("integer digit count: %d\n", integerDigitCount)
fmt.Printf("factional digit count: %d\n", fractionalDigitCount)
bf := big.NewFloat(0.0)
precision := uint(math.Ceil(float64(fractionalDigitCount) * math.Log2(10.0)) + float64(integerDigitCount) * math.Log2(10.0))
fmt.Printf("calculated precision %d\n", precision)
bf, _ = bf.SetPrec(uint(precision)).SetString(pi)
ppi := bf.Text('f', fractionalDigitCount)
fmt.Printf("%s\n", ppi)

also according to the documentation and my testing, a negative value for precision in the bf.Text() method will generate the minumim amount of digits to represent the fractional part.

ppi := bf.Text('f', -1)

Will always generate the exact same output as the input without you having to store the number of fractional digits to use later.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.