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
Answer from Toni Villena on Stack OverflowThe 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
just tested this solution and also added one more feature:
func lbsToGrams(lbs float64) (grams float64) {
return lbs * conversionWeight
}
Find out more on my github here
proposal: strconv: add ParseFloatPrefix
go - Convert string to float32? - Stack Overflow
strconv.ParseFloat() faster altrernatives
parsing - Golang ParseFloat not accurate in example - Stack Overflow
I am parsing huge json files with my own parser where all values are float, which I use in math calculations further on. pprof profiling says 60% of the time is used by the strconv.ParseFloat(). I looked into the code of the ParseFloat() and it's pretty comprehensive, not much clue on my side. Also I checked github and goolged it, but no results suggesting anything else. I believe the strconv.ParseFloat() is probably pretty optimized but nevertheless, I just wonder if anyone has some faster alternative suggestions to the strconv.ParseFloat()? Thanks..
Go uses IEEE-754 binary floating-point numbers. Floating-point numbers are imprecise. Don't use them for financial transactions. Use integers.
For example,
package main
import (
"fmt"
"strconv"
"strings"
)
func parseCents(s string) (int64, error) {
n := strings.SplitN(s, ".", 3)
if len(n) != 2 || len(n[1]) != 2 {
err := fmt.Errorf("format error: %s", s)
return 0, err
}
d, err := strconv.ParseInt(n[0], 10, 56)
if err != nil {
return 0, err
}
c, err := strconv.ParseUint(n[1], 10, 8)
if err != nil {
return 0, err
}
if d < 0 {
c = -c
}
return d*100 + int64(c), nil
}
func main() {
s := "79.35"
fmt.Println(parseCents(s))
s = "149.20"
fmt.Println(parseCents(s))
s = "-149.20"
fmt.Println(parseCents(s))
s = "149.2"
fmt.Println(parseCents(s))
}
Playground: https://play.golang.org/p/mGuO51QWyIv
Output:
7935 <nil>
14920 <nil>
-14920 <nil>
0 format error: 149.2
Based on @peterSO's answer, with some bugfix and enhancement:
https://play.golang.org/p/YcRLeEJ7lTA
package main
import (
"fmt"
"strconv"
"strings"
)
func parseCents(s string) (int64, error) {
var ds string
var cs string
n := strings.SplitN(s, ".", 3)
switch len(n) {
case 1:
ds = n[0]
cs = "0"
case 2:
ds = n[0]
switch len(n[1]) {
case 1:
cs = n[1] + "0"
case 2:
cs = n[1]
default:
return 0, fmt.Errorf("invalid format:%s", s)
}
default:
return 0, fmt.Errorf("invalid format:%s", s)
}
d, err := strconv.ParseInt(ds, 10, 0)
if err != nil {
return 0, err
}
c, err := strconv.ParseUint(cs, 10, 0)
if err != nil {
return 0, err
}
cents := d * 100
if strings.HasPrefix(s, "-") {
cents -= int64(c)
} else {
cents += int64(c)
}
return cents, nil
}
func main() {
examples := map[string]int64{
"79.35": 7935,
"149.20": 14920,
"-149.20": -14920,
"149.2": 14920,
"-0.12": -12,
"12": 1200,
"1.234": 0,
"1.2.34": 0,
}
for s, v := range examples {
cents, err := parseCents(s)
fmt.Println(cents, cents == v, err)
}
}
Your best bet is the use the standard library's strconv. You can make your own wrappers and locale stuff. Eventually I'd add more error checking and turn this into it's own package, but here is an idea. If you haven't found a package yet there is a good chance you will have to write your own. For a more general solution, you'd have to think about every possible input.. normalize that per locale and enforce those rules when others are using your tools... That would be a more complex solution given the number of if statements and pieces of logic.. The good part is that you know the type of input strconv.ParseFloat expects.. So all you really have to do is take the user input and transform it to the programmatic standard http://floating-point-gui.de/formats/fp/. Given numbers are mostly universal, with the exception of commas and decimal points, there shouldn't be many use cases. You might even be able to generalize further and say there are two main formats.. https://www.quora.com/Why-do-some-countries-use-a-period-and-others-use-a-comma-to-separate-large-numbers, which is largely broken down to Europe et al and British/American, where German uses the standard almost all of Europe does. Under that assumption there isn't really much to do as the use cases comes down to 2.
package main
import (
"fmt"
"log"
"strconv"
"strings"
)
func normalizeGerman(old string) string {
s := strings.Replace(old, ",", ".", -1)
return strings.Replace(s, ".", "", 1)
}
func normalizeAmerican(old string) string {
return strings.Replace(old, ",", "", -1)
}
var locale map[string]func(string) string
func init() {
locale = make(map[string]func(string) string)
locale["DE-DE"] = normalizeGerman
locale["US"] = normalizeAmerican
}
func main() {
var f, f2 float64
var err error
// german
if val, ok := locale["DE-DE"]; ok {
f, err = strconv.ParseFloat(val("1.234,87"), 64)
if err != nil {
log.Fatal("german fail", err)
}
}
//american
if val, ok := locale["US"]; ok {
f2, err = strconv.ParseFloat(val("1,234.87"), 64)
if err != nil {
log.Fatal("american fail", err)
}
}
fmt.Println(f, f2)
}
/x/text unfortunately doesn't (publicly) expose everything you need for this, but I was able to leverage it to build something that works pretty well and integrates with /x/text for picking locale
// Locale-aware number parsing with lxstrconv
//
// Docs: https://godoc.org/tawesoft.co.uk/go/lxstrconv
package main
import (
"fmt"
"golang.org/x/text/language"
"tawesoft.co.uk/go/lxstrconv"
)
func checked(f float64, e error) float64 {
if e != nil {
panic(e)
}
return f
}
func main() {
dutch := lxstrconv.NewDecimalFormat(language.Dutch)
british := lxstrconv.NewDecimalFormat(language.BritishEnglish)
arabic := lxstrconv.NewDecimalFormat(language.Arabic)
fmt.Printf("%f\n", checked(british.ParseFloat("1,234.56")))
fmt.Printf("%f\n", checked(dutch.ParseFloat("1.234,56")))
fmt.Printf("%f\n", checked(arabic.ParseFloat("١٬٢٣٤٫٥٦")))
}