Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lost number precision when saving to redis #135

Open
KSDaemon opened this issue Oct 31, 2017 · 5 comments
Open

Lost number precision when saving to redis #135

KSDaemon opened this issue Oct 31, 2017 · 5 comments

Comments

@KSDaemon
Copy link

Hi!

I've found a vary bad thing... as i have investigated, lua-resty-redis loose 64bit number precision, when saving numbers to redis.

See examples below. Lets assume, we have a number 1824192940134586

REDIS CLI CODE

127.0.0.1:6379[5]> set BIG_NUM_AS_NUM_REDIS 1824192940134586
OK
127.0.0.1:6379[5]> get BIG_NUM_AS_NUM_REDIS
"1824192940134586"

Everything works as expected

Now lets work with number through lua-resty-redis

NGINX LUA CODE

-- first, lets save a number in redis
redis:set("BIG_NUM_AS_NUM", 1824192940134586)
redis:set("BIG_NUM_AS_STR", string.format("%16.0f",1824192940134586))

-- now lets get keys
ngx.log(ngx.ERR, "BIG_NUM_AS_NUM: ", redis:get("BIG_NUM_AS_NUM"))
-- prints: 1.8241929401346e+15
ngx.log(ngx.ERR, "BIG_NUM_AS_NUM(tostr): ", string.format("%16.0f",redis:get("BIG_NUM_AS_NUM")))
-- prints: 1824192940134600 - FAIL
ngx.log(ngx.ERR, "BIG_NUM_AS_STR: ", redis:get("BIG_NUM_AS_STR"))
-- prints: 1824192940134586 - OK

ngx.log(ngx.ERR, "BIG_NUM_AS_NUM_REDIS: ", redis:get("BIG_NUM_AS_NUM_REDIS"))
-- prints: 1824192940134586 - OK
ngx.log(ngx.ERR, "BIG_NUM_AS_NUM_REDIS(tostr): ", string.format("%16.0f",redis:get("BIG_NUM_AS_NUM_REDIS")))
-- prints: 1824192940134586 - OK
@KSDaemon
Copy link
Author

KSDaemon commented Oct 31, 2017

What do you think about this solution:
in function _gen_req(args) process numbers with string.format instead of tostring?

        if type(arg) == "number" then
            arg = string.format("%18.0f",arg)
        elseif type(arg) ~= "string" then
            arg = tostring(arg)
        end

Of course it not a full solution. just an idea, because there are may different type of numbers

@spacewander
Copy link
Member

Interesting, it seems that the format result of tostring is awful sometimes:

[3] lua(main)> tonumber(tostring(1824192940134586)) == 1824192940134586
=> false
[4] lua(main)> tonumber(tostring(1824192940134586)) == 1824192940134600
=> true

@spacewander
Copy link
Member

I guess the tostring(num) in x64 is equal to string.format("%.14g", num).
See https://github.com/openresty/luajit2/blob/v2.1-agentzh/src/lj_strfmt_num.c#L589

@KSDaemon
Copy link
Author

My updated proposal for a check:

        local arg = args[i]
        if type(arg) ~= "string" then
            if type(arg) == "number" and arg == math.floor(arg) then
                arg = string.format("%.0f",arg)
            else
                arg = tostring(arg)
            end
        end

@agentzh
Copy link
Member

agentzh commented Oct 31, 2017

@KSDaemon In your redis-cli example, you are using a string literal value instead of a number value. So why don't you just use a string value in Lua too? Lua's native numbers do not support 64-bit precision anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants