语法

1
INCRBYFLOAT key increment

可用版本

≥ 2.6.0

时间复杂度

$O(1)$

ACL类别

@write@string@fast

将存储在 key 上的数值(字符串表示)加上浮点数 increment。

如果 key 不存在,那么 INCRBYFLOAT 会先将 key 的值设置为 0,然后再执行加法操作。

key 的值可以是整数或双精度浮点数。increment 也可以是整数或双精度浮点数。

如果发生以下情况之一,将返回一个错误:

  1. 键包含一个错误类型的值(不是一个字符串)。
  2. 当前键的内容或指定的增量不能被解析为双精度浮点数字。

如果命令执行成功,相加后的值将被存储为键的新值(取代旧值),并作为字符串返回给调用者。

key 的值和增量参数都可以以指数形式提供,但增量操作之后计算得到的最终值将按照整数部分、(如果需要)后跟小数点和代表小数部分的一定位数数字的格式存储。末尾的零总是被移除。也就是说:

1)原值和增量可以使用指数表示法,也可以不使用;

2)但是计算后的最终值会被存储为一个整数(如果需要),后跟小数点和一定位数的小数部分;

3)如果最终值有末尾的零,这些零会被移除掉。

例如:

原值:1.23e5,增量:4e2,最终值:123400

原值:123000,增量:400,最终值:123400

原值:1.230,增量:0.04,最终值:1.27(去掉了末尾的零)

所以简单来说,就是运算后的值会被“规范化”为一个固定的数字格式进行存储。

不管计算的实际内部精度如何,输出的精度固定为小数点后的 17 位。

返回值

返回 key 的新值,这个新值是一个双精度浮点数。

实现细节

该命令总是作为 SET 操作在复制链路和 Append Only File 中传播,所以底层浮点数学实现的差异不会成为不一致的源头。

也就是说:

1)无论原值和增量是否使用了浮点数或指数表示法,这个增量命令最终都是作为 SET 命令进行传播和存储的。

2)因为存储和传播的都是 SET 命令设置的固定值,而不是浮点数值运算的结果,所以即使不同机器上的浮点数学运算结果略有不同(由于计算精度不同等原因),最终存储的值仍然是一致的。

3)如果直接存储和传播浮点数值运算的结果,那么由于计算精度的差异,不同机器得到的结果可能会有细微出入,导致数据不一致。但现在通过 SET 命令设置一个固定的值,就避免了这个问题。

所以这个设计是为了避免因为浮点数运算的实现差异在复制和存储时导致数据不一致,采取设置固定值的方式来规避这个问题。

总之,重点是:不直接存储和传播浮点数运算结果,而是存储和传播 SET 固定值指令,以此来防止由于浮点数运算差异引起的数据不一致。

示例 1

对不存在的键 float_a 执行 INCRBYFLOAT 操作:

1
2
3
4
5
redis> EXISTS float_a
(integer) 0
redis> INCRBYFLOAT float_a 3.14
"3.14"
redis>

示例 2

对存储值为整数类型的键执行 INCRBYFLOAT 操作:

1
2
3
4
5
redis> SET int_a 10
OK
redis> INCRBYFLOAT int_a 0.01
"10.01"
redis>

示例 3

对存储值为字符串类型,但不能表示为数值的的键执行 INCRBYFLOAT 操作:

1
2
3
4
5
6
7
redis> SET username "Jack"
OK
redis> GET username
"Jack"
redis> INCRBYFLOAT username 0.1
(error) ERR value is not a valid float
redis>

示例 4

存储的数值为指数形式、增量参数也为指数形式,执行 INCRBYFLOAT 操作:

1
2
3
4
5
6
7
8
9
redis> SET exp "314159e-5"
OK
redis> GET exp
"314159e-5"
redis> INCRBYFLOAT exp "2e-1"
"3.34159"
redis> GET exp
"3.34159"
redis>

示例 5

执行 INCRBYFLOAT 操作后,新的值会删除小数点后面多余的 0:

1
2
3
4
5
6
7
8
9
redis> SET v 0.100
OK
redis> GET v
"0.100"
redis> INCRBYFLOAT v 0.100
"0.2"
redis> GET v
"0.2"
redis>

(END)