encoding/json mistakenly transfer int64 format to string

188 views
Skip to first unread message

Zhaoxun Yan

unread,
Apr 11, 2022, 11:01:48 PM4/11/22
to golang-nuts
The scenario is upon receiving an incoming financial quotation, save it as a string of json into a Redis service. Sorry but I cannot provide the whole code of quotation receiving here, which is very complex with cgo. But the code below will help you get a glimpse on what should be going on:

import (
    "encoding/json"
    //"errors"
    "fmt"
    "time"
   
)

var pool *redis.Pool

type Fvprices struct {
    P float64 `json:"price"`
    F float64 `json:"floor"`
    C float64 `json:"ceiling"`
    S float64 `json:"settle"`
    T int64   `json:"time"`
}

func init() {
    pool = newPool()
}

var redisport = "6379"
var redisip = "127.0.0.1"
var password = ""

func newPool() *redis.Pool {

    fmt.Println("redis @", redisport, redisip, password)
    return &redis.Pool{ 
        MaxIdle:     4,
        MaxActive:   50, // max number of connections
        IdleTimeout: 30 * time.Second,

        Dial: func() (redis.Conn, error) {
            c, err := redis.DialURL("redis://" + redisip + ":" + redisport)
            if err != nil {
                ErrMsg = fmt.Sprintf("redis connection error: %s", err.Error())
                fmt.Println(time.Now().Format("2006-01-02 15:04:05"), ErrMsg)
                return nil, err
            }
            if _, autherr := c.Do("AUTH", password); autherr != nil {
                ErrMsg = fmt.Sprintf("redis password error: %s", err.Error())
                fmt.Println(time.Now().Format("2006-01-02 15:04:05"), ErrMsg)
                return nil, autherr
            }
            return c, nil
        },
    }
}

func Upfutureprice(future_id string,
    future_price, lowerLimitPrice, upperLimitPrice, preSettlementPrice float64,
    updatetime time.Time) {

    c := pool.Get()
    if c == nil {
        return
    }
    defer c.Close()

    content := Fvprices{
        P: future_price,
        F: lowerLimitPrice,
        C: upperLimitPrice,
        S: preSettlementPrice,
        T: updatetime.UnixNano() / 1e6,
    }

    js, _ := json.Marshal(content)

    if _, err := c.Do("SET", future_id, js); err != nil {
        fmt.Println("cannot save to redis:", err)
    }
}

So obviously until the function "Upfutureprice" everything is correct, for all  four prices it receives are in float64 format. After running this program for one day, I just browse the redis using AnotherRedisDesktopManager via ssh port forwarding, and something strange happens as I clicking on various future_id key strings:

{
price:807
floor:720.6
ceiling:881
settle:"800.8000000000001"
time:1649726499000
}

{
price:"3691.0000000000005"
floor:3237
ceiling:4204
settle:3721
time:1649726910500
}

{
price:"15405.000000000004"
floor:13625
ceiling:17340
settle:15485
time:1649728303500
}

{
price:"800.4000000000001"
floor:720.6
ceiling:881
settle:"800.8000000000001"
time:1649728048000
}

Note quotations above. I wonder how encoding/json can made transformation from a float64 inside struct Fvprices  to a string instead? It seems that only long decimals would trigger such an error while short decimals won't:

{
price:2910
floor:2443.5
ceiling:3305.5
settle:2874.5
time:1649728261026
}

How could that happen? I am really puzzled.

Regards,
    Zhaoxun

Steven Hartland

unread,
Apr 12, 2022, 9:57:14 AM4/12/22
to Zhaoxun Yan, golang-nuts
First off, the package you're using for redis isn't maintained; you should switch to github.com/gomodule/redigo/redis instead, which will allow you to remove the c == nil check as that doesn't happen.

In your example you're ignoring error from json.Marshal which could be hiding a problem, so I would recommend you handle that.

encoding/json should represent float as a json number so I would never expect what you're seeing but its not clear to me if that is down to how you are viewing it.

--
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/f42b29b3-de17-48c6-9f71-1176f1288396n%40googlegroups.com.

Zhaoxun Yan

unread,
Apr 13, 2022, 2:35:13 AM4/13/22
to Steven Hartland, golang-nuts
Thank you Steven.
I am a little bewildered by  the new mod configuration. Will it compile If I download the source file from the new github source to src directory without further setting up a mod in the old fashioned way? I am using go1.15.

And today I ran a test logging all the sendings to Redis and readings from Redis. It is fine - nothing went wrong. But AnotherRedisDesktopManager still shows the same string format - which I guess is due to it is 32bit and fail to transfer a long decimal to float32.

So nothing is wrong in golang or its open-source package.

Brian Candler

unread,
Apr 13, 2022, 3:11:54 AM4/13/22
to golang-nuts
> I am a little bewildered by  the new mod configuration.

It's as simple as this:

go mod init blah
go mod tidy

You can put anything(*) for "blah". Literally "blah" will work. It's just whatever name you want to give your module. However if you plan to publish your module on github then using "github.com/name/project" allows it to be found automatically (**).

"go mod tidy" reads through your source code, finds the imported dependencies, and fetches them for you.  After that, a "go build" or "go run" will work.  If you add or remove dependencies, run "go mod tidy" again.

These commands create "go.mod" and "go.sum" files. They become part of your source code - i.e. you should check them in if you are using a source control system.

If you create additional packages in subdirectories, then those packages are referenced in import statements as <modulename>/<subdir>. You only need one go.mod at the top level.

HTH,

Brian.

(*) OK, not quite anything: there are some lexical constraints.
(**) See

Michael Ellis

unread,
Apr 14, 2022, 12:50:14 PM4/14/22
to golang-nuts
@Brian
That example is a superbly concise explanation go mod. Thanks! And I had no idea you could specify filenames and modules in the Playground.  That's really useful!

Brian Candler

unread,
Apr 14, 2022, 3:55:48 PM4/14/22
to golang-nuts
"txtar" is the magic.

In the playground, there's a dropdown on the right - it says "Hello, World!" by default.  From here, you can select the example called "Multiple Files".
Reply all
Reply to author
Forward
0 new messages