Converting string input to float64 using ParseFloat in Golang - go

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

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!

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

Does converting a string to a float lose precision?

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!

Parsing a time with the format HHMMSS00

I'm working with some data from multiple sources and one of these sources is a Sage ERP system.
I am trying to reference two files in Sage in particular, an audit date and audit time (AUDTDATE and AUDTTIME).
I need to parse this and store it as a DATETIME in a Microsoft SQL Server database.
Currently, I am just trying to figure out the best way to parse this.
An example of what the data might look like is below:
+----------+----------+
| AUDTDATE | AUDTTIME |
+----------+----------+
| 20170228 | 5013756 |
+----------+----------+
AUDTDATE is a yyyymmdd format and the AUDTTIME is HHMMSS00.
So I tried the below as a test:
func main() {
value := "20170228 5013756"
layout := "20060102 15040500"
t, _ := time.Parse(layout, value)
fmt.Println(t)
}
This doesn't work, it just returns 0001-01-01 00:00:00 +0000 UTC when run.
If I change the time to this 050137 and the layout to 150405 then this works fine:
func main() {
value := "20170228 050137"
layout := "20060102 150405"
t, _ := time.Parse(layout, value)
fmt.Println(t)
}
One way that I can think of to deal with this is to strip the milliseconds off from the end and then check the length and add a zero to the beginning if it needs one.
This seems like a pretty ugly solution and would involve doing something like this:
func main() {
date := "20170228"
timeString := "5013756"
value := date + prepareTime(timeString)
layout := "20060102150405"
t, _ := time.Parse(layout, value)
fmt.Println(t)
}
func prepareTime(time string) string {
if len(time) == 7 {
time = "0" + time
}
return time[:6]
}
Is there a way to do this without going through the above? Perhaps natively with the time package?
Assuming that you're pulling back 2 separate values from the DB, you can use fmt.Sprintf to 0 pad timeString. Combining it with the date string, you can use the following:
value := fmt.Sprintf("%s %08s", date, timeString)[:15]
In your code:
func main() {
date := "20170228"
timeString := "5013756"
value := fmt.Sprintf("%s %08s", date, timeString)[:15]
layout := "20060102 150405"
t, _ := time.Parse(layout, value)
fmt.Println(t)
}
Results:
2017-02-28 05:01:37 +0000 UTC
This approach is useful because it will also correctly pad any shorter value of time, e.g. 13756 will be converted to 00013756.
The fmt.Sprintf function is useful to format arguments into a string using the formatting you desire as specified by a format string and a list of arguments (...interface{}). The format string tells the function how to render the arguments.
This format string uses two items of note:
String verb (%s): The format string uses a variety of verbs that are used for string substitutions. %s is specifically to render a string or a slice. Other popular verbs include %d for base 10 integer and %f for float with a complete list in the docs. The %v verb is very useful can also be used here as it will render an argument's default value.
0 left padding: To 0 left pad an argument, use 0 followed by a length number in the verb after the %. This will prepended the argument with a maximum number of 0s specified in the length number. For example, %08s will render a string with up to 8 prepended zeros. This means a string "" will be "00000000" while a string "1234567" will result in "01234567". If the string is longer than the length, nothing will be prepended.
From the documentation:
%s the uninterpreted bytes of the string or slice
0 pad with leading zeros rather than spaces;
for numbers, this moves the padding after the sign
More detailed is available in the documentation: https://golang.org/pkg/fmt/

Convert string to float32?

There is an input that I need to read from the console as a string, then manipulate the string and convert some of it to float32.
I have tried using:
float, _ := strconv.ParseFloat(myString, 32)
But it does not work. This is the error I get:
cannot use float (type float64) as type float32 in field value
Is there anything else I could do?
Thanks!
float has the type float32, but strconv.ParseFloat returns float64. All you need to do is convert the result:
// "var float float32" up here somewhere
value, err := strconv.ParseFloat(myString, 32)
if err != nil {
// do something sensible
}
float = float32(value)
Depending on the situation, it may be better to change float's type to float64.

Resources