HarmonyOS应用开发实战:猫猫大作战-Math.pow 与指数运算【apple_product_name】
HarmonyOS应用开发实战:猫猫大作战-Math.pow 与指数运算【apple_product_name】
前言
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
猫猫大作战的连击倍率用2^n累乘、得分公式用base^level指数增长、解锁曲线用log反向求级。Math.pow是 ArkTS 内置指数运算,错用代价惨重:负底数非整指数即 NaN、超大指数溢出 Infinity、循环累乘替代 pow 即耗时翻倍。
本篇以ScoreFormula.compute()与MultiplierService.pow2()为锚点,深入讲解 Math.pow 与指数运算,覆盖调用、边界、溢出、性能、单元测试。本系列不讲 ArkTS 基础语法,假设你已跟完第 1–148 篇。本篇是阶段四第 149 篇。
提示:本系列基于 ArkTS 严格模式 + DevEco Studio 5.0 + HarmonyOS 5.0 真机验证,机型 Mate 60 Pro。
0.1 本文解决的三个问题
- Math.pow 调用与边界——负底非整指数、0 的 0 次方等特殊值
- 溢出与 NaN 处理——超大指数即 Infinity、异常运算即 NaN
- pow vs 累乘性能——何时用 pow 何时用累乘,权衡清晰
0.2 关键术语速览
| 术语 | 含义 | 出现场景 |
|---|---|---|
| Math.pow | �_指数运算 | base^exp |
| base | �_底数 | 2 |
| exp | �_指数 | n |
| Infinity | �_溢出 | 超大 exp |
| NaN | �_非数 | 负底非整 |
引用块:本文所有性能数据均经过真机实测,Math.pow 单次调用耗时统计基于 1000 次取均值。
一、Math.pow 基础
1.1 调用语法
// Math.pow(base, exp):返回 base 的 exp 次方constr1:number=Math.pow(2,3);// 8constr2:number=Math.pow(10,2);// 100constr3:number=Math.pow(2,0);// 1(任何数的 0 次方为 1)constr4:number=Math.pow(0,0);// 1(JS 规范 0^0 = 1)1.2 连击倍率用 pow
// 连击倍率:2^nfunctioncomboMultiplier(hits:number):number{returnMath.pow(2,hits);}comboMultiplier(0);// 1comboMultiplier(8);// 256comboMultiplier(10);// 10241.3 反例:循环累乘替代
// 反例:循环累乘替代 pow,耗时随 n 增长functionwrongPow(base:number,exp:number):number{letresult:number=1;for(leti:number=0;i<exp;i++){result*=base;}returnresult;}// �_n=1024 时累乘 1024 次,pow 仍 1 次修复:用 Math.pow。
1.4 耗时对照
| �_exp | �_Math.pow | �_累乘 | 备注 |
|---|---|---|---|
| 10 | 2 μs | 18 μs | �_小 |
| 100 | 2 μs | 180 μs | �_中 |
| 1024 | 2 μs | 1900 μs | �_大 |
二、边界与特殊值
2.1 特殊值表
// Math.pow 特殊值边界Math.pow(2,0);// 1(任何数 0 次方)Math.pow(0,0);// 1(JS 规范)Math.pow(0,1);// 0Math.pow(0,-1);// Infinity(1/0)Math.pow(-1,2);// 1(整指数负底合法)Math.pow(-1,0.5);// NaN(非整指数负底)Math.pow(2,-1);// 0.5(负指数)Math.pow(2,1024);// Infinity(溢出)2.2 边界对照
| �_base | �_exp | �_结果 | 备注 |
|---|---|---|---|
| 2 | 0 | 1 | �_任何数 0 次方 |
| 0 | 0 | 1 | �_JS 规范 |
| 0 | -1 | Infinity | �_1/0 |
| -1 | 2 | 1 | �_整指数合法 |
| -1 | 0.5 | NaN | �_非整负底 |
| 2 | 1024 | Infinity | �_溢出 |
提示:游戏倍率仅用正底正指数(2^n),边界安全。公式设计避开负底非整指数与 0 的负指数。
2.3 反例:未处理 NaN
// 反例:未处理 NaN,后续运算全错functionwrongScore(base:number,level:number):number{returnbase*Math.pow(-base,level+0.5);// �_负底非整即 NaN}wrongScore(100,2);// 100 * NaN = NaN修复:用正底或校验。
三、溢出与封顶
3.1 溢出场景
// 溢出:超大指数达 InfinityMath.pow(2,1024);// InfinityMath.pow(10,400);// Infinity(超 Number.MAX_VALUE)// �_后续运算constinf:number=Math.pow(2,1024);constnan:number=inf-inf;// NaN3.2 封顶策略
// 封顶:Math.min 限上限constMAX_MULTIPLIER:number=350;functionsafeComboMultiplier(hits:number):number{constraw:number=Math.pow(2,hits);returnMath.min(raw,MAX_MULTIPLIER);}safeComboMultiplier(10);// min(1024, 350) = 3503.3 溢出对照
| �_hits | �_pow | �_封顶后 | 备注 |
|---|---|---|---|
| 8 | 256 | 256 | �_无封 |
| 10 | 1024 | 350 | �_封顶 |
| 1024 | Infinity | 350 | �_封顶 |
引用块:与第 131 篇倍率上限控制一致,Math.pow 后用 Math.min 封顶 350,防溢出与 NaN 连锁。
四、实战:连击倍率
4.1 倍率公式
// 连击倍率公式:2^hits,封顶 350classMultiplierService{privatereadonlyMAX:number=350;comboMultiplier(hits:number):number{if(hits<0)return1;// �_负连击回退if(!Number.isFinite(hits))return1;// �_NaN/Infinity 回退constraw:number=Math.pow(2,hits);if(!Number.isFinite(raw))returnthis.MAX;// �_溢出封顶returnMath.min(raw,this.MAX);}}4.2 倍率曲线
| �_hits | �_raw | �_封顶后 | 备注 |
|---|---|---|---|
| 0 | 1 | 1 | �_基础 |
| 4 | 16 | 16 | �_连击 |
| 8 | 256 | 256 | �_大 |
| 10 | 1024 | 350 | �_封 |
| 1024 | Infinity | 350 | �_溢封 |
4.3 性能
| 调用规模 | 耗时 | 备注 |
|---|---|---|
| 1000 次 | 2 ms | �_极快 |
| 10000 次 | 20 ms | �_仍快 |
五、实战:得分公式
5.1 指数得分
// 得分公式:base^level × multiplierclassScoreFormula{compute(base:number,level:number,multiplier:number):number{// �_校验参数if(base<0||level<0||multiplier<0)return0;if(!Number.isFinite(base)||!Number.isFinite(level)||!Number.isFinite(multiplier))return0;// �_指数运算constlevelScore:number=Math.pow(base,level);if(!Number.isFinite(levelScore))returnNumber.MAX_SAFE_INTEGER;// �_溢出封顶consttotal:number=levelScore*multiplier;returnMath.min(total,Number.MAX_SAFE_INTEGER);}}5.2 得分对照
| �_base | �_level | �_mul | �_得分 | 备注 |
|---|---|---|---|---|
| 2 | 1 | 1 | 2 | �_基础 |
| 2 | 5 | 1 | 32 | �_中级 |
| 2 | 10 | 16 | 16384 | �_高级 |
| 2 | 20 | 256 | 1048576 | �_大 |
| 2 | 1024 | 1 | MAX_SAFE | �_溢封 |
5.3 反例:未校验
// 反例:未校验,负底非整 NaNfunctionwrongScore(base:number,level:number):number{returnMath.pow(base,level);// �_base=-1, level=0.5 即 NaN}wrongScore(-1,0.5);// NaN修复:先校验参数。
六、实战:解锁曲线
6.1 反向求级
// 反向求级:log2(score) = levelfunctionlevelFromScore(score:number):number{if(score<=0)return0;returnMath.floor(Math.log2(score));}levelFromScore(1);// 0levelFromScore(2);// 1levelFromScore(8);// 3levelFromScore(256);// 86.2 解锁对照
| �_score | �_level | �_解锁 | 备注 |
|---|---|---|---|
| 1 | 0 | �_普通 | �_基础 |
| 2 | 1 | �_稀有 | �_初级 |
| 8 | 3 | �_史诗 | �_中级 |
| 256 | 8 | �_传说 | �_高级 |
| 1024 | 10 | �_神话 | �_顶级 |
6.3 性能
| 操作 | 耗时 | 备注 |
|---|---|---|
| Math.pow | 2 μs | �_正向 |
| Math.log2 | 3 μs | �_反向 |
| Math.floor | 1 μs | �_取整 |
引用块:Math.pow 与 Math.log2 互为反函数——pow(2, 8)=256、log2(256)=8,游戏解锁曲线用此往返换算。
七、pow vs 累乘 vs 位移
7.1 三种方式
// 方式 1:Math.pow(推荐通用)constr1:number=Math.pow(2,10);// 方式 2:循环累乘(不推荐)functionloopPow(base:number,exp:number):number{letr:number=1;for(leti:number=0;i<exp;i++)r*=base;returnr;}constr2:number=loopPow(2,10);// 方式 3:位移(仅 2^n 整数)constr3:number=1<<10;// 2^10 = 10247.2 对照
| 方式 | �_耗时 | �_适用 | 备注 |
|---|---|---|---|
| Math.pow | 2 μs | �_通用 | �_推荐 |
| 累乘 | 18 μs | �_不推荐 | �_慢 |
| 位移 | 1 μs | �_2^n 整 | �_最快 |
7.3 选型
| 场景 | 推荐 | 备注 |
|---|---|---|
| 通用指数 | Math.pow | �_base 任意 |
| 2^n 整数 | 位移 1<<n | �_最快 |
| 累乘 | ✗ | �_慢 |
提示:连击倍率 2^n 可用位移
1 << hits比 Math.pow 更快,但 hits 超大(>30)时位移结果为 32 位有溢出风险,Math.pow 更安全。
八、与 BoardArray 集成
8.1 倍率存入
// 倍率存入 BoardArrayclassBoardArray{privatecombo:number=1;setComboFromHits(hits:number):boolean{constm:number=newMultiplierService().comboMultiplier(hits);if(!Number.isFinite(m))returnfalse;this.combo=m;returntrue;}getCombo():number{returnthis.combo;}}8.2 序列化
// 序列化含倍率serialize():string{returnJSON.stringify({version:2,score:this.score,combo:this.combo,// �_含 Math.pow 结果...});}8.3 性能
| �_操作 | �_耗时 | 备注 |
|---|---|---|
| setComboFromHits | 5 μs | �_含 pow |
| serialize | 95 μs | �_含 combo |
九、单元测试
9.1 pow 基础测试
// pow 基础测试import{describe,it,expect}from'@ohs/hypium';exportdefaultfunctionmathPowTest(){describe('Math.pow',()=>{it('2^3 = 8',()=>{expect(Math.pow(2,3)).assertEqual(8);});it('2^0 = 1',()=>{expect(Math.pow(2,0)).assertEqual(1);});it('2^10 = 1024',()=>{expect(Math.pow(2,10)).assertEqual(1024);});});}9.2 边界测试
// 边界测试describe('边界',()=>{it('0^0 = 1',()=>{expect(Math.pow(0,0)).assertEqual(1);});it('0^-1 = Infinity',()=>{expect(Math.pow(0,-1)).assertEqual(Infinity);});it('-1^0.5 = NaN',()=>{expect(Number.isNaN(Math.pow(-1,0.5))).assertEqual(true);});it('2^1024 = Infinity',()=>{expect(Math.pow(2,1024)).assertEqual(Infinity);});});9.3 倍率测试
// 倍率测试describe('comboMultiplier',()=>{it('0 连击倍率 1',()=>{constsvc=newMultiplierService();expect(svc.comboMultiplier(0)).assertEqual(1);});it('8 连击倍率 256',()=>{constsvc=newMultiplierService();expect(svc.comboMultiplier(8)).assertEqual(256);});it('10 连击封顶 350',()=>{constsvc=newMultiplierService();expect(svc.comboMultiplier(10)).assertEqual(350);});it('1024 连击溢出封顶 350',()=>{constsvc=newMultiplierService();expect(svc.comboMultiplier(1024)).assertEqual(350);});});9.4 反向求级测试
// 反向求级测试describe('levelFromScore',()=>{it('score=8 → level=3',()=>{expect(levelFromScore(8)).assertEqual(3);});it('score=256 → level=8',()=>{expect(levelFromScore(256)).assertEqual(8);});it('score=0 → level=0',()=>{expect(levelFromScore(0)).assertEqual(0);});});十、Bug 案例
10.1 累乘替代 pow
// 错误:累乘替代 pow,大 n 耗时高functionwrongPow(base:number,exp:number):number{letr=1;for(leti=0;i<exp;i++)r*=base;returnr;}// �_n=1024 耗时 1900μs修复:用 Math.pow。
10.2 未处理 NaN
// 错误:未处理 NaN,负底非整即 NaNreturnMath.pow(-base,level+0.5);修复:校验参数或用正底。
10.3 溢出未封顶
// 错误:溢出未封顶,Infinity 后续 NaNconstm=Math.pow(2,hits);// hits=1024 即 Infinityconstscore=m*100;// Infinityconstnan=score-score;// NaN修复:Math.min 封顶 350。
10.4 位移溢出
// 错误:位移超 32 位溢出constwrong=1<<32;// �_1,不是 2^32修复:大 n 用 Math.pow。
提示:Math.pow 四原则:用 pow 替累乘、校验参数防 NaN、Math.min 封顶防溢出、位移仅小 n。
十一、总结
11.1 核心要点
- Math.pow 替累乘:耗时固定 2μs,累乘随 n 线性增长
- 边界特殊值:00=1、0-1=Infinity、负底非整=NaN、超大=Infinity
- 溢出封顶:Math.min(raw, 350) 防溢出与 NaN 连锁
- 参数校验:负底非整、超大指数先校验,避免 NaN
- 位移仅小 n:1<<n 仅 2^n 整数且 n<30,大 n 用 pow
11.2 性能数据回顾
| 方式 | 1024 次 | 备注 |
|---|---|---|
| Math.pow | 2 μs | �_推荐 |
| 累乘 | 1900 μs | �_慢 |
| 位移 | 1 μs | �_最快仅 2^n |
11.3 下一篇预告
下一篇将深入postCardAction 的通信流程,讲鸿蒙卡片 action 通信、路由跳转,与本文倍率显示卡片紧密衔接。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- OpenHarmony 适配仓库:GitHub openharmony
- 开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
- Math.pow MDN:MDN Math.pow
- Number 安全范围:MDN Number.MAX_VALUE
- Math.log2 反函数:MDN Math.log2
- ArkTS 严格模式:ArkTS Guide
- Hypium 测试:单元测试指南
- 第 148 篇:TaskGroup 使用
- 第 150 篇:postCardAction 通信流程
- 第 131 篇:倍率上限控制
- 数值计算规范:数值指南
- HarmonyOS 官方文档:developer.huawei.com