EP040 Number.RoundDown:数值向下取整与舍入计算(阶梯截断与整箱矩阵重构)

文 / 老汪(汽车零部件财务与商业智能实战派)
专栏:【Excel BI 财务建模实战课讲义(第2阶段)】 · 期数:EP040
核心函数:Number.RoundDown / Number.RoundUp / Number.Round / Table.FromColumns


📌 老汪开篇导读:车间现场的“装箱整列”与数值舍入陷阱

各位财务同仁、成本会计、计划调度员与仓管工程师:

在咱们汽车零部件制造企业(Tier 1 一级供应商)的日常生产管控中,几乎所有精密加工件、底盘衬套、制动卡钳活塞,从冲压机加出来到成品包装,都受制于一个刚性工业指标——标准包装规格(SNP,Standard Number of Packaging,即标包箱规)。

比如今天总装车间下达了一张工单:某批次生产了 75 件 铝合金底盘控制臂液压衬套,而专用的防静电周转箱规格严格限定为 20 件/箱。

这时候,仓库发料员和现场调度拿着一长串 单列 75 行 的零件序列号流水账,急冲冲跑到财务办公室拍桌子:

“老汪,客户主机厂和装配线要求发料交接单必须按‘周转箱’并排打印!每 20 件摆一列,第 1 箱 20 件排一列、第 2 箱 20 件排一列……最后一箱不足 20 件是尾数箱。现在几百条流水挤在一列里,工人根本没法逐箱核对装箱!”

很多初级分析员遇到这种需求,第一反应是:
– 手工一段一段复制剪切粘贴成多列(一旦工单数量变成 120 件,全部返工重做);
– 或者在 Excel 里拼命写 OFFSET、INDEX、MOD、INT 等复杂数组公式,只要行数动态变化,公式要么报 #REF!,要么卡死崩溃。

在 Power Query 进阶工程中,将“单列长流水”动态重构为“按箱规并排的多列矩阵看板”,最核心的算子正是用于精确定位批次与分块切片的截断函数——Number.RoundDown!

今天老汪不仅要带大家彻底吃透 Number.RoundDown、Number.RoundUp、Number.Round 三大舍入函数的底层运算逻辑,更要教会大家一套在 Power Query 内存中直接完成“整箱切片与多列合成”的高阶架构技法!


🛠️ 第一部分:老法师白话底层——数值舍入“三兄弟”原理剖析

在 M 语言中,处理数值的保留位数与取整,主要依托以下三个函数:

Number.RoundDown(number as nullable number, optional digits as nullable number) as nullable number
Number.RoundUp(number as nullable number, optional digits as nullable number) as nullable number
Number.Round(number as nullable number, optional digits as nullable number) as nullable number

1. Number.RoundDown(向下舍入 / 去尾法)

  • 核心逻辑:严格向数值变小的方向截断(即去尾),绝不进行四舍五入。
  • 参数特性:
  • 第 1 参数:目标数值;
  • 第 2 参数(可选):保留的小数位数。若缺省不填,默认取整数位(相当于保留 0 位小数)。
  • 实测表现:
  • Number.RoundDown(1.88) ➔ 返回 1(不管小数点后是 8 还是 9,通通去尾舍弃);
  • Number.RoundDown(1.88, 1) ➔ 返回 1.8(保留 1 位小数,后位直接抹平);
  • Number.RoundDown(-1.2) ➔ 返回 -2(注意:向负无穷方向向下取整!这是很多财务容易混淆的数学陷阱)。
  • 制造场景用途:
  • 计算满装箱数、整托盘数;
  • 采购阶梯返利门槛(未达到整百万梯度的部分不计阶梯返点);
  • 设备实际有效折旧整月数。

2. Number.RoundUp(向上舍入 / 进一法)

  • 核心逻辑:只要存在被舍去的非零余数,就向数值增大的方向进一位。
  • 实测表现:
  • Number.RoundUp(1.12) ➔ 返回 2;
  • Number.RoundUp(1.82, 1) ➔ 返回 1.9。
  • 制造场景用途:
  • 物流运费干线车辆排车(75 吨货物,每辆货车荷载 20 吨,必须派 Number.RoundUp(75/20) = 4 辆车,不能派 3 辆);
  • 采购包装辅料(螺丝密封袋一包 100 只,领用 105 只必须申领 2 包整包)。

3. Number.Round(四舍五入 —— 🚨 银行家舍入法踩坑警告)

  • 老汪敲黑板(致命差异):
  • Excel 工作表公式 =ROUND(2.5, 0) 返回 3,属于算术四舍五入;
  • 但是!Power Query M 语言中的 Number.Round 默认采用国际标准的「银行家舍入法(Banker’s Rounding / 四舍六入五成双)」!
  • 遇到 .5 时,它会舍入到最接近的偶数:
    • Number.Round(2.5) ➔ 返回 2(因为 2 是偶数);
    • Number.Round(3.5) ➔ 返回 4(因为 4 是偶数)。
  • 如果财务月结算税金或成本时盲目套用 Number.Round,会导致账目与传统总账系统出现几分钱的尾差!

🏭 第二部分:汽车零部件装配工单“整箱发料矩阵”实战

1. 业务场景背景

某汽车底盘控制臂总成车间,工单 WO-20261109-001 共完成下线 75 件 铝合金底盘控制臂液压衬套(零件图号 P-8820-BUSH,序列号 SN-202611-B08-001 ~ 075)。
– 标准包装规格(SNP):20 件/箱;
– 目标输出:将单列 75 行的流水表,重构为 4 列的装箱矩阵:
– 箱号01 (满箱20件):对应第 1 ~ 20 件;
– 箱号02 (满箱20件):对应第 21 ~ 40 件;
– 箱号03 (满箱20件):对应第 41 ~ 60 件;
– 箱号04 (尾数箱15件):对应第 61 ~ 75 件,末尾剩余 5 行自动填充为 null。

2. 核心数学转换逻辑

我们要把流水切片成列,首先要用 List.Range 截取子列表:
– 第 1 箱:从下标 0 开始,取 20 个;
– 第 2 箱:从下标 20 开始,取 20 个;
– 第 3 箱:从下标 40 开始,取 20 个;
– 第 4 箱:从下标 60 开始,取 20 个。

那么,一共需要多少个箱号索引呢?
总件数是 75,每箱 20 件。如果我们直接写 75 / 20 = 3.75,向下取整是 3。
但如果总件数恰好是 80 件(正好 4 个满箱) 时:
– 80 / 20 = 4,若写生成 {0..4},会产生下标 0, 1, 2, 3, 4 共 5 列,第 5 列变成全空!
– 老法师工程级边界防溢出公式:
powerquery
最大箱索引 = Number.RoundDown((总件数 - 1) / 箱规)

– 当件数=75 时:(75 - 1) / 20 = 3.7 ➔ 向下舍入为 3,生成 {0, 1, 2, 3}(共 4 箱);
– 当件数=80 时:(80 - 1) / 20 = 3.95 ➔ 向下舍入为 3,生成 {0, 1, 2, 3}(共 4 箱满箱,绝不溢出空列);
– 当件数=81 时:(81 - 1) / 20 = 4.0 ➔ 向下舍入为 4,自动进入第 5 箱!

这就是 Number.RoundDown 在数组切片与看板重构中的工程级妙用!


💻 第三部分:随堂课件完整 M 脚本工程实现

💡 老汪敲黑板(免改路径直接刷新秘籍):
平时做财务分析最怕换台电脑一刷新就报错“找不到文件路径”。老汪在给各位学员配套的实操课件【配置】表 A1 单元格中预置了自适应公式,大家把本课案例包解压后直接点击 Excel 的【全部刷新】即可秒级重现,绝不报红字!大家可以把这套自适应架构无缝迁移到公司的日常模型中! 实操指南点此篇文章:《实操指南》

// =========================================================================
// 专栏:【Excel BI 财务建模实战课讲义(第2阶段)】· EP040
// 模块:Number.RoundDown 与整箱发料多列矩阵重构
// 讲师:老汪(汽车零部件财务与商业智能实战派)
// =========================================================================

let
    // ---------------------------------------------------------------------
    // 步骤 1:【Q001 自适应路径规范】动态定位当前工作簿同级目录
    // ---------------------------------------------------------------------
    当前目录 = try 
        let 
            rawPath = Text.From(Excel.CurrentWorkbook(){[Name="当前路径"]}[Content]{0}[Column1]),
            cleanPath = Text.Trim(rawPath),
            fixedPath = if Text.EndsWith(cleanPath, "\") then cleanPath else cleanPath & "\"
        in 
            fixedPath
    otherwise 
        "./",

    // ---------------------------------------------------------------------
    // 步骤 2:动态读取同级「原始业务数据表.xlsx」中底盘支架装配工单流水
    // ---------------------------------------------------------------------
    数据文件路径 = 当前目录 & "原始业务数据表.xlsx",
    源文件 = Excel.Workbook(File.Contents(数据文件路径), null, true),
    流水表 = 源文件{[Item="底盘支架装配工单流水", Kind="Sheet"]}[Data],
    提升的标题 = Table.PromoteHeaders(流水表, [PromoteAllScalars=true]),
    更改的类型 = Table.TransformColumnTypes(提升的标题, {
        {"序号", Int64.Type}, 
        {"零件序列号", type text}, 
        {"零件图号", type text}, 
        {"零件名称", type text}, 
        {"批次工单号", type text}, 
        {"工序名称", type text}, 
        {"质检结论", type text}
    }),

    // ---------------------------------------------------------------------
    // 步骤 3:提取待分箱流水字段与计算参数
    // ---------------------------------------------------------------------
    序列号清单 = 更改的类型[零件序列号],
    总件数 = Table.RowCount(更改的类型), // 当前批次 75 件
    箱规 = 20,                          // 标准周转箱装载量 (SNP: 20件/箱)

    // ---------------------------------------------------------------------
    // 步骤 4:核心算子 Number.RoundDown 精准计算最大箱号索引
    // 【老汪解析】:
    // 使用 (总件数 - 1) / 箱规,无论整除与否,都能精准锚定 0-based 最大箱号索引
    // ---------------------------------------------------------------------
    最大箱索引 = Number.RoundDown((总件数 - 1) / 箱规),
    箱号列表 = {0 .. 最大箱索引},

    // ---------------------------------------------------------------------
    // 步骤 5:按箱切片,提取各箱零部件列表
    // ---------------------------------------------------------------------
    各箱列表 = List.Transform(
        箱号列表,
        each List.Range(序列号清单, _ * 箱规, 箱规)
    ),

    // ---------------------------------------------------------------------
    // 步骤 6:生成各箱动态表头(如:箱号01 (满箱20件)、箱号04 (尾数箱)...)
    // ---------------------------------------------------------------------
    动态列名 = List.Transform(
        箱号列表,
        each "箱号" & Text.PadStart(Text.From(_ + 1), 2, "0") & 
             (if (_ + 1) * 箱规 <= 总件数 then " (满箱" & Text.From(箱规) & "件)" else " (尾数箱)")
    ),

    // ---------------------------------------------------------------------
    // 步骤 7:多列列表一键合成为标准发料矩阵表格
    // ---------------------------------------------------------------------
    发料矩阵表 = Table.FromColumns(各箱列表, 动态列名)
in
    发料矩阵表

📊 第四部分:清洗前后数据效果对照表

1. 原始单列工单流水表(部分节选)

序号 零件序列号 零件图号 零件名称 批次工单号 工序名称 质检结论
1 SN-202611-B08-001 P-8820-BUSH 铝合金底盘控制臂液压衬套 WO-20261109-001 压装与激光打码工序 合格
2 SN-202611-B08-002 P-8820-BUSH 铝合金底盘控制臂液压衬套 WO-20261109-001 压装与激光打码工序 合格
… … … … … … …
75 SN-202611-B08-075 P-8820-BUSH 铝合金底盘控制臂液压衬套 WO-20261109-001 压装与激光打码工序 合格

2. 最终输出:整箱发料多列矩阵看板(4列并排,尾数箱自动补空)

箱号01 (满箱20件) 箱号02 (满箱20件) 箱号03 (满箱20件) 箱号04 (尾数箱)
SN-202611-B08-001 SN-202611-B08-021 SN-202611-B08-041 SN-202611-B08-061
SN-202611-B08-002 SN-202611-B08-022 SN-202611-B08-042 SN-202611-B08-062
SN-202611-B08-003 SN-202611-B08-023 SN-202611-B08-043 SN-202611-B08-063
… (以此类推满20行) … (以此类推满20行) … (以此类推满20行) … (15件零件)
SN-202611-B08-020 SN-202611-B08-040 SN-202611-B08-060 (第16~20行自动显示为空)

🚦 第五部分:老汪实战避坑指南(三大血泪教训)

  1. 别用除法直接取整,警惕“恰好整箱”时的空列幽灵:
    – 很多初学者写 Number.RoundDown(总行数 / 箱规),当总行数正好是 40、箱规 20 时,算出 2;如果写 {0 .. 2},就会产生 0, 1, 2 共 3 个切片!第 3 箱切片 List.Range(列表, 40, 20) 会返回一个空列表,导致最终矩阵表格多出一整列刺眼的空白!
    – 牢记老汪的工程公式:Number.RoundDown((总行数 - 1) / 箱规),不论整除与否,永远精准锁定有效分块数量!
  2. 警惕 Table.FromColumns 的长短列自适应特性:
    – Table.FromColumns 接收一个由列表组成的列表(List of Lists)。当各列长度不一致时(如前 3 箱有 20 行,第 4 箱只有 15 行),M 引擎会自动以最长的一列为基准,并在短列末尾自动填充 null;
    – 这也是为什么在构建多列矩阵看板时,Table.FromColumns 远比 Table.FromRows 更加优雅稳定的核心原因。
  3. 负数舍入与日期周期折旧:
    – 财务计算超期存货库龄或延保扣款时,若出现负数天数,务必注意 Number.RoundDown(-0.1) 返回的是 -1 而非 0。如果需要向零的方向截断,应使用 Number.IntegerDivide(整数除法截断)配合正负判断。

🎁 老汪结语与随堂课件包下载

这套“数值截断计算 + 动态下标列表 + 列表切片 + 多列表格合成”的组合技,是每一位 Excel BI 财务建模工程师迈向中高级开发者的分水岭。大家只要吃透了这套逻辑,无论是车间装箱看板、按固定行数打印凭证套打模板、还是多列对比报表,都能在几十行 M 脚本中游刃有余地动态驾驭!

老汪已经把本节课涉及的原始业务数据表、最终完成版带PQ查询工程文件以及学员操作指引指南全部打包上传。大家在文末点击即可免费下载实操演练!

[评论可见]隐藏内容 – 管理员可见
📦

EP040 随堂实操课件与 M 脚本原码包

本压缩包内含本节讲解使用的 Excel 随堂课件、M 脚本源码及清洗前后对照表。
已配置绝对自适应相对路径,下载解压后即可直接一键刷新演练!

📁 文件名:EP040_Number_RoundDown_数值向下取整与整箱矩阵重构_课件包.zip


🚀 立即下载随堂课件包 (.zip)

© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容