Does converting a string to a float lose precision? - go

Problem
When converting a string to a float64, the fractional part of the float64 loses a significant amount of numbers.
Code
origVal := "0.00000628"
convVal, err := strconv.ParseFloat(origVal, 64)
if err == nil {
fmt.Printf("Original value: %s\nConverted value: %f\n", origVal, convVal)
}
Outputs:
Original value: 0.00000628
Converted value: 0.000006
The code is available on the Go Playground: https://play.golang.org/p/a8fH_JGug7l
Context
I am pulling data from an API. This API stringifies floating point numbers. I convert these stringified numbers to floats because I want to do some basic arithmetics on them.
I am fairly new to Go, so my apologies if the answer is straightforward.

The problem was not that the string was not correctly converted, but that Printf does, by default, not output the complete fractional part if it is long.
The following code prints the same as the original code but with 10 numbers after the decimal point:
origVal := "0.00000628"
convVal, err := strconv.ParseFloat(origVal, 64)
if err == nil && err2 ==nil {
fmt.Printf("Original value: %s\nConverted value: %.10f\n", origVal, convVal)
}
Thanks to #usr2564301 for the quick reply!

Related

How do I parse a currency value as *big.Int in Go?

I want to parse a string like "12.49" into a *big.Int in Go. The resulting *big.Int should represent the amount of cents in the given value, in this case 1249. Here are some more examples of inputs and their expected outputs:
"3": 300
"3.1": 310
".19": 19
I already tried working with *big.Float and its Int function, but realized, that *big.Float does not provide arbitrary precision.
Right now I'm using this algorithm, but it seems fragile (Go Playground link):
func eurToCents(in string) *big.Int {
missingZerosUntilCents := 2
i := strings.Index(in, ".")
if i > -1 {
missingZerosUntilCents -= len(in) - i - 1
if missingZerosUntilCents < 0 {
panic("too many decimal places")
}
}
in = strings.Replace(in, ".", "", 1)
in += strings.Repeat("0", missingZerosUntilCents)
out, ok := big.NewInt(0).SetString(in, 10)
if !ok {
panic(fmt.Sprintf("could not parse '%s' as an interger", in))
}
return out
}
Is there a standard library function or other common way to parse currencies in Go? An external library is not an option.
PS: I'm parsing Nano cryptocurrency values, which have 30 decimal places and a maximum value of 133,248,297.0. That's why I'm asking for *big.Int and not uint64.
Update: Seems like this solution is still buggy, because an inaccurate result is reported after multiplication: https://play.golang.org/p/RS-DC6SeRwz
After revisiting the solution with *big.Float, I realized, that it does work perfectly fine. I think I forgot to use SetPrec on rawPerNano previously. I'm going to provide an example for the Nano cryptocurrency, because it requires many decimal places.
This works as expected (Go Playground link):
func nanoToRaw(in string) *big.Int {
f, _ := big.NewFloat(0).SetPrec(128).SetString(in)
rawPerNano, _ := big.NewFloat(0).SetPrec(128).SetString("1000000000000000000000000000000")
f.Mul(f, rawPerNano)
i, _ := f.Int(big.NewInt(0))
return i
}
Thanks #hymns-for-disco for nudging me in the right direction!

Testing an expression evaluator in Go

I have written a small constant number expression evaluator. I’m running go-fuzz on it, but it can only detect crashes.
I would like to test that expression considered invalid by my program are really invalid, and valid expression are really valid and yield a correct result.
How could I do that in Go ?
I looked into existing packages like this one, but it allows much more operations and types than I currently support. It thus can’t use it for validation.
The values that I handle are int and float64, and the operations are | & ^ ~(inverse) + - * / %. Also I support the same type of number literals as Go.
I did manual checks but this doesn’t bring me very far. I would need to check the result against another expression evaluator.
You can use go/types and co.:
Constant folding computes the exact constant value (constant.Value)
for every expression (ast.Expr) that is a compile-time constant. Use
Info.Types[expr].Value for the results of constant folding.
I'll use Info.Defs instead of Info.Types as that's simpler for this case, IMO at least.
expr := "float64(20)/6.8"
// parse the expression as part of a file so that it can be type checked
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "x.go", "package x\nconst K = "+expr, 0)
if err != nil {
panic(err)
}
// type check the file so that the constant value gets computed
info := types.Info{Defs: map[*ast.Ident]types.Object{}}
conf := types.Config{}
if _, err := conf.Check("x", fset, []*ast.File{f}, &info); err != nil {
panic(err)
}
// get the value (as string)
for id, obj := range info.Defs {
if id.Name == "K" {
v := obj.(*types.Const).Val()
fmt.Println(v.String())
}
}
https://play.golang.org/p/h2e0No2F1CY

What's the best way to parse a float value from []bytes?

I have a function that simply reads a file with ioutil.ReadFile(). The type returned is []byte, although the value itself can be represented as a float.
I am converting the []byte in this manner (where value is the []byte being returned from a function that reads a file):
var floatValue float64
fmt.Fscanf(bytes.NewReader(value), "%f", &floatValue)
Is this really the only way to extract/parse a valid float value from a []byte? There's a similar discussion but looks like it didn't really go anywhere.
You can easily use strconv.ParseFloat for this, just converting your []byte to a string first. This would surely have less overhead than creating a reader and scanning with a scanf-like function.
sb := []byte("3.1415")
s := string(sb)
f, err := strconv.ParseFloat(s, 64)
if err != nil {
panic("whoops!")
}
fmt.Printf("%f\n", f)
Output:
3.141500

Converting string input to float64 using ParseFloat in Golang

I've just started learning Go and I'm trying to convert a string from standard input to a float64 so I can perform an arithmetic operation on the input value.
The output returns "0 feet converted to meters gives you 0 meters" regardless of the input value. I can't figure out why the value is zero after invoking ParseFloat on the input.
If someone could please point out to me why this is happening, I would greatly appreciate it.
const conversion float64 = 0.3048
func feetToMeters (feet float64) (meters float64) {
return feet * conversion
}
func main(){
fmt.Println("\n\nThis program will convert feet to meters for you!\n")
reader := bufio.NewReader(os.Stdin)
fmt.Println("Enter feet value: \n")
feet, _ := reader.ReadString('\n')
feetFloat, _ := strconv.ParseFloat(feet, 64)
meters := feetToMeters(feetFloat)
fmt.Printf("%v feet converted to meters give you %v meters",feetFloat,meters)
}
The problem is that you try to parse "x.x\n", e.g: 1.8\n. And this returns an error: strconv.ParseFloat: parsing "1.8\n": invalid syntax. You can do a strings.TrimSpace function or to convert feet[:len(feet)-1] to delete \n character
With strings.TrimSpace() (you need to import strings package):
feetFloat, _ := strconv.ParseFloat(strings.TrimSpace(feet), 64)
Wtih feet[:len(feet)-1]:
feetFloat, _ := strconv.ParseFloat(feet[:len(feet)-1], 64)
Output in both cases:
10.8 feet converted to meters give you 3.2918400000000005 meters

How do I convert a float in the format of xxxxxxxe+09 to an int in Go?

Given a number like 1.400126761e+09 in Golang, what can I use to cast it to an int? I tried using the strconv library to play around with it and convert it using FormatFloat but that function returns the same thing when I give it the 'e' flag. Any other functions/libraries that will handle this conversion to an int?
Just use int(). For example:
x := float32(3.1)
fmt.Println(int(x))
ParseFloat is not returning the same thing, it's returning a float64 or float32. After you use it, you can just convert to an int as usual:
s := "1.400126761e+09"
f, err := strconv.ParseFloat(s, 64)
if err == nil {
thisisanint := int(f)
fmt.Println(thisisanint)
} else {
fmt.Println(err)
}
Go Playground
I actually was not clear as the variable I was playing with employs the interface{} and simply needed a float64 type assertion before casting it like int(). Hope this helps!

Resources