StarRocks pmod 函数详解:返回正余数的取模运算
2026/9/19 6:30:36 网站建设 项目流程

StarRocks pmod 函数详解:返回正余数的取模运算

【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks

pmod是 StarRocks 内置的数学函数,用于返回「被除数 ÷ 除数」的正余数(positive remainder),其结果的符号与除数保持一致,与同目录下的mod(结果符号与被除数一致)形成互补。本文以 pmod 官方英文文档 为骨架,结合 BE 端向量化实现 与 FE 端函数注册源码,系统讲解 pmod 的语法、类型规则、返回值语义、完整示例及其底层实现原理,帮助你正确地在 SQL 中使用取模运算处理周期性计算、分桶取余、循环索引等场景。

pmod 是什么:与取模/余数的区别

在数学中,取模(modulo)的结果通常定义为非负数。然而在多数编程语言中,%运算符保留被除数的符号,例如 C/C++、Java 中-11 % 5 == -1。StarRocks 提供两个语义不同的取余函数:

  • mod(dividend, divisor):结果符号与被除数一致(详见 mod 官方文档);
  • pmod(dividend, divisor):结果符号与除数一致,当除数为正时结果恒为非负数,即「正余数」。

从 BE 端整数实现 可以清楚看到两者的关系:

  • modImpl直接返回a % (b + (b == 0)),保留被除数符号;
  • pmodImpl在此基础上再做一次「加 b 再取模」变换((a % (b + (b == 0))) + b) % (b + (b == 0)),把结果搬移到与除数同号的区间内。

也就是说,pmod(a, b)的符号由b决定:b > 0时结果 ∈[0, |b|)b < 0时结果 ∈(-|b|, 0]

函数语法与参数说明

pmod(dividend, divisor)

参数定义

参数含义
dividend被除数(the number to be divided)
divisor除数(the number that divides)

两个参数支持以下数据类型:

  • BIGINT
  • DOUBLE

注意

dividenddivisor的数据类型必须一致;若不一致,StarRocks 会进行隐式类型转换。实际使用中建议显式保持一致(例如统一使用 DOUBLE 或 BIGINT),避免隐式转换带来的精度或语义意外。

从 FE 端函数注册 可以看到,pmod是 StarRocks 内建标量函数之一(public static final String PMOD = "pmod"),可直接在 SQL 中调用,无需任何 UDF 注册步骤。

返回值规则

  • 返回与dividend相同数据类型的值;
  • divisor0时返回NULL

「除数为 0 返回 NULL」这一语义在 BE 端实现 中由RValueCheckZeroImpl承担:它检查右操作数b == 0,命中后由VectorizedUnstrictBinaryFunction框架将结果置为 NULL,而不是抛错或产生硬件除零异常。

使用示例

以下示例直接取自 pmod 官方文档 并在 StarRocks 的 MySQL 兼容客户端中验证:

mysql> select pmod(3.14,3.14); +------------------+ | pmod(3.14, 3.14) | +------------------+ | 0 | +------------------+ mysql> select pmod(3,6); +------------+ | pmod(3, 6) | +------------+ | 3 | +------------+ mysql> select pmod(11,5); +-------------+ | pmod(11, 5) | +-------------+ | 1 | +-------------+ mysql> select pmod(-11,5); +--------------+ | pmod(-11, 5) | +--------------+ | 4 | +--------------+ mysql> SELECT pmod(11,-5); +--------------+ | pmod(11, -5) | +--------------+ | -4 | +--------------+ mysql> SELECT pmod(-11,-5); +---------------+ | pmod(-11, -5) | +---------------+ | -1 | +---------------+

观察这组结果可以归纳出规律:

表达式结果说明
pmod(3.14, 3.14)0浮点被除数,返回 DOUBLE 类型的 0
pmod(3, 6)33 < 6,正余数即被除数本身
pmod(11, 5)1常规正数取模:11 = 2×5 + 1
pmod(-11, 5)4除数为正,结果非负:-11 = -3×5 + 4
pmod(11, -5)-4除数为负,结果与除数同号:11 = -3×(-5) + (-4)
pmod(-11, -5)-1除数为负,结果与除数同号:-11 = 2×(-5) + (-1)

源码剖析:pmod 在 StarRocks 中如何实现

向量化入口:按类型分派

在 be/src/exprs/math_functions.h#L366-L375 中,pmod 以向量化模板函数DEFINE_VECTORIZED_FN(pmod)暴露,按列类型Type分派到两套实现:

if constexpr (Type == TYPE_FLOAT || Type == TYPE_DOUBLE) { return VectorizedUnstrictBinaryFunction<RValueCheckZeroImpl, pmodFloatImpl>::evaluate<Type>(l, r); } else { return VectorizedUnstrictBinaryFunction<RValueCheckZeroImpl, pmodImpl>::evaluate<Type>(l, r); }
  • FLOAT / DOUBLE 列走pmodFloatImpl(基于 C 标准库fmod);
  • 其余类型(含 BIGINT)走pmodImpl(基于整数%运算)。

两者都包裹在VectorizedUnstrictBinaryFunction<RValueCheckZeroImpl, ...>中,由RValueCheckZeroImpl统一负责「除数为 0 返回 NULL」的语义,保证了整数与浮点路径行为一致。

整数实现与 SIGFPE 防护

pmodImpl 的完整实现为:

DEFINE_BINARY_FUNCTION_WITH_IMPL(pmodImpl, a, b) { // Guard against SIGFPE: on x86 the idiv instruction raises #DE when computing // TYPE_MIN % -1 (the quotient overflows the result width). pmod(a, -1) == 0 for // every a, so short-circuit before the hardware divide. The operator path in // arithmetic_operation.h already carries this guard; mirror it for the function. if (b == -1) { return ResultType(0); } return ((a % (b + (b == 0))) + b) % (b + (b == 0)); }

这段代码蕴含了两个关键工程细节:

  1. -1除数的短路保护:x86 的idiv指令在计算TYPE_MIN % -1时会因商溢出结果位宽而触发#DE异常(表现为 SIGFPE)。数学上任意a-1取模结果恒为0,因此实现中先行短路返回0,避免硬件除零信号。modImpl(math_functions.h#L591-L597)同样携带了这一防护。
  2. b + (b == 0)的除零兜底:当b == 0时,(b == 0)在 C++ 中求值为1,除数退化为1,从硬件层面规避了%除零;真正的「返回 NULL」语义则由外层RValueCheckZeroImpl在结果写入前拦截完成。

浮点实现

pmodFloatImpl 基于 C 标准库fmod做了同样的「正余数」变换:

DEFINE_BINARY_FUNCTION_WITH_IMPL(pmodFloatImpl, a, b) { return ::fmod(::fmod(a, (b + (b == 0))) + b, (b + (b == 0))); }

先用fmod得到与a同号的余数,再加b平移、再fmod一次,把结果约束到与b同号的区间。这也解释了文档示例中pmod(3.14, 3.14)返回0而非浮点噪声值。

与 mod、fmod 的横向对比

StarRocks 在 math-functions 文档目录 中同时提供了modfmod,三者容易混淆:

函数结果符号规则支持类型典型实现
mod(dividend, divisor)被除数一致(C 风格取余)TINYINT/SMALLINT/INT/BIGINT/LARGEINT/FLOAT/DOUBLE 等a % b(math_functions.h#L591-L597)
pmod(dividend, divisor)除数一致(数学风格取模)BIGINT、DOUBLE((a % b) + b) % b
fmod(dividend, divisor)被除数一致的浮点余数(IEEE 754)FLOAT、DOUBLEC 标准库::fmod

选择建议

  • 需要结果为非负数(如哈希分桶、循环索引、周期性任务编号)时使用pmod
  • 需要与 C/Java 的%语义对齐时使用mod
  • 需要精确的浮点余数(如三角函数周期规约)时使用fmod

典型应用场景与注意事项

典型场景

  • 分桶与取余路由:用pmod(hash_value, bucket_num)把任意整数值映射到[0, bucket_num)的桶号,天然规避负数键带来的负桶号问题;
  • 周期编号pmod(day_index, 7)生成稳定的 0~6 周内索引,day_index为负(如历史日期)时结果依旧落在合法区间;
  • 数据脱敏/抽样pmod(id, 100) < 10作为确定性抽样条件,结果不受 id 正负影响。

注意事项

  1. 类型一致性:BIGINT 与 DOUBLE 混用时依赖隐式转换,建议显式统一(例如CAST(a AS DOUBLE));
  2. 除数为 0:返回NULL,在条件判断中需配合IS NULL处理,避免NULL参与后续运算导致结果不可见;
  3. 负数除数pmod在除数为负时结果也为负(如pmod(11, -5) = -4),若业务期望恒非负,应保证除数为正;
  4. 类型覆盖范围:pmod 仅支持 BIGINT 与 DOUBLE(区别于mod对更宽整数类型的支持),超大整数运算建议先用CAST确认类型。

小结

pmod是 StarRocks 处理取模运算时「保证符号可控」的关键函数:文档层面明确了「结果与除数同号、除数为 0 返回 NULL」的语义;BE 源码 则展示了向量化框架下整数/浮点双实现、除零兜底与 SIGFPE 防护等工程细节。需要对照阅读时,可参考英文原文档 pmod.md 或其中文译本 中文 pmod 文档,并结合 mod.md、fmod.md 做语义对比。

【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询