九个常用的数学函数:abs、mod、round、truncate、sign、pi、rand、ceil、
floor。
两个重点:round(3.789, 2) 给 3.79 而 truncate 给 3.78(一个进位一个砍);
ceil(-4.1) 给 -4、floor(-4.9) 给 -5——
「向上」「向下」是往数轴哪边走,不是绝对值变大变小。
mod(-7, 3) 这里给 2,MySQL 给 -1——余数跟谁走,两边规定不同。
上一篇讲了函数的通用规矩,这一篇开始一个个过。 数学函数是最「稳」的一类——参数是数字,返回值是数字, 没有编码、没有时区、没有排序规则那些麻烦。 但有两个地方照样会翻车:小数取整的方向、负数取余的符号。
一、abs 与 sign:绝对值与正负号
abs(-3.5) 给 3.50,负变正、正的不动。
sign(x) 给 -1、0、1 三个值。 判断正负不用写 case when,一句话就够。
它还有个用处:把「差多少」变成「往哪边差」,做趋势图时很顺手。
二、mod:取余,但余数的符号两边不一样
mod(7, 3) 给 1,
两边一样。 但 mod(-7, 3) 这里给 2,MySQL 给 -1。 这是本章第二处「同名不同结果」。
差别在「余数跟谁走」:这里(SQLite)跟除数走,结果永远非负;
MySQL 跟被除数走,结果与被除数同号。
什么时候会踩:拿余数当「桶号」分页、分片、哈希分组时,负数一出现就会分到负桶,
MySQL 那边直接报错或越界。 要么保证被除数非负,要么写 abs() 兜一层。
三、round 与 truncate:一个进位,一个直接砍
round(3.789, 2) 给 3.79——看第三位是 9,进一位。
truncate(3.789, 2) 给 3.78——第三位直接扔掉。
0.5 这一档最能看出差别:round(2.5) 给 3.00,truncate(2.5, 0) 给 2.00。
负数同理,round(-2.5) 给 -3.00、truncate(-2.5, 0) 给 -2.00。
规律:truncate 永远让绝对值变小(往零的方向砍),
round 不一定(往远离零的方向进)。
算钱用 round(该进就进),算「不超过某个上限」用 truncate。
四、ceil 与 floor:向上取整、向下取整
ceil(4.1) 给 5、floor(4.9) 给 4,这两个符合直觉。
负数就反直觉了:ceil(-4.1) 给 -4、floor(-4.9) 给 -5。
「向上」是往数轴右边走(数值变大),不是往绝对值大的方向走。
这一处最容易搞反。 记法:ceil 就是天花板,天花板永远在上面;
floor 是地板,地板永远在下面——画在数轴上,负数也一样成立。
五、pi、pow 与 rand
pi() 给 3.141592653589793,它是少数不要参数的函数。 pow(2, 10) 给 1024.00。
rand() 在第 5 章讲过了:不给种子每次都变,给了种子就固定。
它也算数学函数,只是常用在「随机取几条」上。
还有几个没演示但值得知道:sqrt() 开方、exp() 自然指数、log() 对数。
用到的场景不多,查手册比记着强。
六、动手 Demo:从绝对值到取整,重点看两个方向
Demo 六段:abs/sign、mod、round/truncate、ceil/floor、pi/pow。
每段都在盯着符号与方向——这一篇的坑全在「负数是往哪边走」。
import _q46 as Q
c = Q.con()
c.execute("SELECT ABS(-3.5) AS a, SIGN(-9) AS s1, SIGN(0) AS s2, "
"SIGN(9) AS s3")
# 取余:负数这一档两边不一样
c.execute("SELECT MOD(7, 3) AS a, MOD(-7, 3) AS b")
# round 进位、truncate 直接砍
c.execute("SELECT ROUND(3.789, 2) AS r2, TRUNCATE(3.789, 2) AS t2")
c.execute("SELECT ROUND(2.5) AS r, TRUNCATE(2.5, 0) AS t, "
"ROUND(-2.5) AS rn, TRUNCATE(-2.5, 0) AS tn")
# 向上取整、向下取整
c.execute("SELECT CEIL(4.1) AS c1, CEIL(-4.1) AS c2, "
"FLOOR(4.9) AS f1, FLOOR(-4.9) AS f2")
# pi 与 pow
c.execute("SELECT CAST(ROUND(PI(), 6) AS TEXT) AS pi6, POW(2, 10) AS p")
实跑输出:
--------------------------------------------------------------------
第二十四课:数学函数,重点在 round 与 truncate 的差别
--------------------------------------------------------------------
abs 与 sign:一个取绝对值,一个取正负号
SQL> SELECT ABS(-3.5) AS a, SIGN(-9) AS s1, SIGN(0) AS s2, SIGN(9) AS s3
a s1 s2 s3
--------------------------
3.50 -1 0 1
(1 行)
**`sign` 给 -1、0、1 三个值**,判断正负不用写 `case when`。
**`abs` 就是绝对值**,负变正,正的不动。
mod:取余,但余数的符号两边不一样
SQL> SELECT MOD(7, 3) AS a, MOD(-7, 3) AS b
a b
--------
1 2
(1 行)
**`7 % 3` 给 1,两边一样。** **`-7` 取余 3 这里给 2,MySQL 给 -1。**
**差别在「余数跟谁走」**:这里跟除数走(结果非负),
**MySQL 跟被除数走(结果与被除数同号)。**
**做分页、分组这类「余数当桶号」的场景,负数一出现就会分错桶。**
round 与 truncate:一个进位,一个直接砍
SQL> SELECT ROUND(3.789, 2) AS r2, TRUNCATE(3.789, 2) AS t2
r2 t2
--------------
3.79 3.78
(1 行)
**3.789 保留两位:`round` 给 3.79(看第三位 9 进位),`truncate` 给 3.78(后面的直接扔掉)。**
SQL> SELECT ROUND(2.5) AS r, TRUNCATE(2.5, 0) AS t, ROUND(-2.5) AS rn, TRUNCATE(-2.5, 0) AS tn
r t rn tn
----------------------------------
3.00 2.00 -3.00 -2.00
(1 行)
**0.5 这一档:`round` 给 3.00 和 -3.00**——往远离零的方向进。
**`truncate` 给 2.00 和 -2.00**——往零的方向砍。
**所以 `truncate` 永远让绝对值变小,`round` 不一定。**
**算钱用 `round`(该进就进),算「不超过某个值」用 `truncate`。**
ceil 与 floor:向上取整、向下取整
SQL> SELECT CEIL(4.1) AS c1, CEIL(-4.1) AS c2, FLOOR(4.9) AS f1, FLOOR(-4.9) AS f2
c1 c2 f1 f2
------------------------
5 -4 4 -5
(1 行)
**4.1 向上取整给 5,-4.1 向上取整给 -4**——**「向上」是往数轴右边走,不是往绝对值大的方向走。**
**`floor(-4.9)` 给 -5,也是往数轴左边走。** **这一处符号最容易搞反。**
pi、pow 与 rand
SQL> SELECT CAST(ROUND(PI(), 6) AS TEXT) AS pi6, POW(2, 10) AS p
pi6 p
---------------------
3.141593 1024.00
(1 行)
**`pi()` 给 3.141592653589793,`pow(2, 10)` 给 1024.00。**
**`rand()` 上一篇讲过:不给种子每次都变,给了种子就固定。**
**`pi()` 不用给参数**,这在 SQL 里算少见的——**大部分函数至少要一个参数。**
第一段。 abs(-3.5) 给 3.50,sign 给 -1/0/1。
第二段取余。 mod(7,3) 给 1,mod(-7,3) 给 2——记住这一格,MySQL 那边是 -1。
第三段进位与砍。 3.79 与 3.78 差一位,就这一位决定账对不对。
第四段 0.5 那一档。 round 给 3.00/-3.00,truncate 给 2.00/-2.00。
第五段取整。 5、-4、4、-5——ceil(-4.1) 是 -4,往上走。
第六段。 pi 六位小数 3.141593,pow(2,10) 给 1024.00。
30 秒实验
ROUND(2.5)与ROUND(3.5)分别给什么?和 Python 的round()一致吗?TRUNCATE(-3.789, 1)给多少?是 -3.7 还是 -3.8?CEIL(-0.5)与FLOOR(-0.5)分别给什么?
常见误区
- 以为
truncate和round差不多。 3.789 保留两位,一个 3.79 一个 3.78; 金额差一分,对账就永远差一分。 - 以为
ceil是「绝对值变大」。ceil(-4.1)给 -4,绝对值变小了——它是往数轴右边走。 - 拿负数取余当桶号。 MySQL 给负余数,直接越界;两个引擎的桶号还会对不上。
自测三问(点开对答案)
1. `round` 与 `truncate` 在 3.789 上分别给什么? —— `round(3.789, 2)` 给 3.79(进位),`truncate(3.789, 2)` 给 3.78(砍掉)。 2. `ceil(-4.1)` 为什么给 -4 而不是 -5? —— `ceil` 是往数轴右边(数值变大)取整,-4 比 -4.1 大,所以是 -4。 3. `mod(-7, 3)` 在本沙箱和 MySQL 分别给什么? —— 本沙箱给 2(余数跟除数走),MySQL 给 -1(余数跟被除数走)。数字过完了,下一篇轮到字符串——第一个坑就在 length 这个函数名上。
- ☐ (本篇完,下一篇 6.3 字符串函数)