16

I am writing a simple Lua script to calculate a median from a Sorted Set (http://redis.io/commands/#sorted_set) within Redis 2.8. The script is below

local cnt = redis.call("ZCARD", KEYS[1])

if cnt > 0 then
        if cnt%2 > 0 then
                local mid = math.floor(cnt/2)
                return redis.call("ZRANGE", KEYS[1], mid, mid)
        else
                local mid = math.floor(cnt/2)
                local vals = redis.call("ZRANGE", KEYS[1], mid-1, mid)
                return (tonumber(vals[1]) + tonumber(vals[2]))/2.0
        end
else
        return nil
end

The problem is the script always returns an integer, when the result should be a float. The result is wrong.

$ redis-cli zrange floats 0 100
1) "1.1"
2) "2.1"
3) "3.1"
4) "3.4975"
5) "42.63"
6) "4.1"

$ redis-cli EVAL "$(cat median.lua)" 1 floats
(integer) 3

The correct result should be (3.1 + 3.4975)/2.0 == 3.298

Adil
  • 2,092
  • 3
  • 25
  • 35

2 Answers2

17

From the documentation for EVAL:

Lua has a single numerical type, Lua numbers. There is no distinction between integers and floats. So we always convert Lua numbers into integer replies, removing the decimal part of the number if any. If you want to return a float from Lua you should return it as a string, exactly like Redis itself does (see for instance the ZSCORE command).

Therefore, you should update your script so you are returning the float as a string:

return tostring((tonumber(vals[1]) + tonumber(vals[2]))/2.0)
  • Thanks! "So we always convert Lua numbers into integer replies, removing the decimal part of the number" - Is there any reason for that, sounds completely counter intuitive. – Adil Dec 13 '13 at 12:04
  • I guess integers are the more common use case than floats in redis. – Azmisov Sep 23 '15 at 16:27
0

sorry i have no explanation of this i encounter this by accidental when making script

function int2float(integer)
  return integer + 0.0
end
Blanket Fox
  • 377
  • 4
  • 15