Having difficulty converting []byte to float

4,897 views
Skip to first unread message

john.ma...@gmail.com

unread,
May 17, 2018, 10:31:11 PM5/17/18
to golang-nuts
Hello all.  I am creating a custom exporter for FreeNAS https://github.com/Maelos/freenas_exporter and am stuck on the conversion of the string of bytes provided by the commands output to a float.  Here is my code, what I have tried, and my results:

What I have tried and results (commented so you can try and see each https://play.golang.org/p/sevfk7Nt2w4

Attempt 1 = binary.LittleEndian.Uint64([]bytes) to math.Float64frombits
Attempt 2 = bytes to string, String to float (strconv)
Attempt 3 = Bytes to bytes.Reader to binary.Read(slice of bytes, binary.LittleEndian, floatVariableToFill) to error check

Please let me know what more I can provide.  I will keep hacking away at it tomorrow, but hoping I can get this done soon.  Full go play write up below:

package main


import (
 
//You may need to edit these in or out depending upon the attempt
 
"bytes"
 
"encoding/binary"
 
"fmt"
 
//"strconv"
 
//"math"
)


func main
() {


 
// I have been able to test this part and I do get a return of "[50 10]" which is correct (converts string "2" for 2 CPUs).  The exact statement may be a bit different.
 
// I am running the script on the latest FreeNAS whic his built from 11.1STABLE FreeBSD.  I have Go installed and am building the files on the shell
 
//numCPUCmd := exec.Command("bash", "-c","/usr/local/bin/ipmitool -I lanplus -H ipmiAddress -U ipmiUser -f /root/ipmi_password sdr elist all | grep -c -i \"cpu.*temp\"")
 
//numCPUBytes, _ := numCPUCmd.Output() //returns a slice of bytes and an error




 
// ATTEMPT 1
 
var flty float64
 sob
:= []byte{50, 10}
 fmt
.Printf("\n%T %v\n\n", sob, sob)


 buf
:= bytes.NewReader(sob)
 err
:= binary.Read(buf, binary.LittleEndian, &flty)
 
if err != nil {
 fmt
.Println("binary.Read failed:", err)
 
}
 fmt
.Println(flty)
 
 
/*Result
 []uint8 [50 10]


 binary.Read failed: unexpected EOF
 0
 */

 
 
////////////////////////////////////////////////////////
 
//ATTEMPT 2
 
/*
 var f float64
    text := []byte{50, 10} // A decimal value represented as Latin-1 text


    f, err := strconv.ParseFloat(string(text), 64)
    if err != nil {
        panic(err)
    }
    fmt.Println(f)
 */

 
/*Result
 panic: strconv.ParseFloat: parsing "2\n": invalid syntax


 goroutine 1 [running]:
 main.main()
 /tmp/sandbox657430918/main.go:44 +0x160
 */

 
 
////////////////////////////////////////////////
 
//ATTEMPT3
 
/*
 sob := []byte{50, 10}
 bits := binary.LittleEndian.Uint64(sob)
 fmt.Printf("\n\n%T %v\n\n", bits, bits)


 flty := math.Float64frombits(bits)
 fmt.Printf("\n\n%T %v\n\n", flty, flty)


 inty := int(flty)
 fmt.Printf("\n\n%T %v\n\n", inty, inty)
 */

 
/* Result
 panic: runtime error: index out of range


 goroutine 1 [running]:
 encoding/binary.binary.littleEndian.Uint64(...)
 /usr/local/go/src/encoding/binary/binary.go:76
 main.main()
 /tmp/sandbox742704811/main.go:62 +0x20
 */

}

Tamás Gulácsi

unread,
May 18, 2018, 12:45:08 AM5/18/18
to golang-nuts
The input is string, so use strconv.ParseFloat, or ParseInt (will there be non-integer number of cpus?)
Just trim the LF with strings.TrimSpace.

For production use, leave out the grep and bash, read the output lines directly with bufio.Scanner.

Jesse McNelis

unread,
May 18, 2018, 1:15:02 AM5/18/18
to john.ma...@gmail.com, golang-nuts
On Fri, May 18, 2018 at 12:26 PM, <john.ma...@gmail.com> wrote:
> Hello all. I am creating a custom exporter for FreeNAS
> https://github.com/Maelos/freenas_exporter and am stuck on the conversion of
> the string of bytes provided by the commands output to a float. Here is my
> code, what I have tried, and my results:
>
> What I have tried and results (commented so you can try and see each
> https://play.golang.org/p/sevfk7Nt2w4

> Attempt 1 = binary.LittleEndian.Uint64([]bytes) to math.Float64frombits

You've got a 2 byte slice, but you need 8 bytes. You're getting an
'index out of range' because you need a slice of 8 bytes.


> Attempt 3 = Bytes to bytes.Reader to binary.Read(slice of bytes,
> binary.LittleEndian, floatVariableToFill) to error check

You've got 2 bytes in your bytes.Reader and you're trying to read 8
bytes from it, this is why you get an error.
A float64 is 8 bytes so you need at least 8 bytes in your bytes.Reader.

> Attempt 2 = bytes to string, String to float (strconv)

You are parsing the string "2\n" which isn't a string representation
of a float. So that's not going to work.
The fact that these bytes are ascii characters means that your other
attempts don't make a lot of sense. The ascii value for the character
'2' is 50 so even if your other attempts worked you'd get a float with
50 instead of the 2 you're expecting.

If you do f, err :=
strconv.ParseFloat(strings.TrimSpace(string(text)), 64) you'll trim
off the invalid '\n' and you'll just have a '2' that will parse
correctly.

john.ma...@gmail.com

unread,
May 18, 2018, 9:18:41 AM5/18/18
to golang-nuts
Thank you both for the quick responses.  I looked in to https://github.com/lovoo/ipmi_exporter yesterday and saw the TrimSpace used as well, but I am still not sure what is adding the '\n' to the commands's Output().

Tamas, I will have to look into thebuff IO, even if I am not using this for production use (home lab), I'd still like to know what you mean behind it (though I think I have a basic understanding).

Jesse, what you state matches up with what I was reading in the packages.  Thank you for helping to clarify and explain it.  I should have recalled how Todd McLeod kept referring to the ASCII wiki/table to see what the bytes referenced.

Thank you two again!  I should be able to get it working today and I will update my github with the working, custom Prometheus metric exporter for FreeNAS.

john.ma...@gmail.com

unread,
May 18, 2018, 11:06:46 AM5/18/18
to golang-nuts
Drilling down farther for the heck of it

os/exec/exec.go
func (c *Cmd) Output() ([]byte, error) { if c.Stdout != nil {

 
return nil, errors.New("exec: Stdout already set")

 
}

 
var stdout bytes.Buffer

 c
.Stdout = &stdout

 captureErr
:= c.Stderr == nil

 
if captureErr {

 c
.Stderr = &prefixSuffixSaver{N: 32 << 10}

 
}

 err
:= c.Run()

 
if err != nil && captureErr {

 
if ee, ok := err.(*ExitError); ok {

 ee
.Stderr = c.Stderr.(*prefixSuffixSaver).Bytes()

 
}

 
}

 
return stdout.Bytes(), err

}

stdout.Bytes() = bytes.Buffer.Bytes()
bytes/buffer.go

// Bytes returns a slice of length b.Len() holding the unread portion of the buffer. // The slice is valid for use only until the next buffer modification (that is, // only until the next call to a method like Read, Write, Reset, or Truncate). // The slice aliases the buffer content at least until the next buffer modification, // so immediate changes to the slice will affect the result of future reads. func (b *Buffer) Bytes() []byte { return b.buf[b.off:] }

// A Buffer is a variable-sized buffer of bytes with Read and Write methods. // The zero value for Buffer is an empty buffer ready to use. type Buffer struct { buf []byte // contents are the bytes buf[off : len(buf)] off int // read at &buf[off], write at &buf[len(buf)] bootstrap [64]byte // memory to hold first slice; helps small buffers avoid allocation. lastRead readOp // last read operation, so that Unread* can work correctly. // FIXME: it would be advisable to align Buffer to cachelines to avoid false // sharing. }

I am still not sure where the "10" or '\n' came from, but I know enough to get the program working. I am really enjoying getting to learn this language and excited for all that I can get it to do, even if it comes with the expected frustrations and head against wall banging.
Message has been deleted

john.ma...@gmail.com

unread,
May 18, 2018, 12:27:39 PM5/18/18
to golang-nuts
After talking with a coworker it seems that "grep" returns a \n after its output.  That was the bugger keeping my initial efforts from working.  Grep was meant for "human consumption".  I will need to look into buffio and scanner as Tamas suggested.

Tashi Lu

unread,
May 18, 2018, 10:44:49 PM5/18/18
to golang-nuts
There is a function for this in the math lib Float64Frombits, a similar question: https://stackoverflow.com/questions/22491876/convert-byte-array-uint8-to-float64-in-golang

Michael Jones

unread,
May 19, 2018, 12:57:31 AM5/19/18
to dotsl...@gmail.com, golang-nuts
yes, the example there is perfect:

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--
Michael T. Jones
michae...@gmail.com
Reply all
Reply to author
Forward
0 new messages