OpenHarmony鸿蒙PC完成ohos-sdk适配自动签名编译rust_decimal三方库,用于高精度十进制浮点场景
欢迎加入开源鸿蒙PC社区: https://harmonypc.csdn.net/
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
AtomGit 仓库地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_rust_cargo
本文讲解鸿蒙 PC 端 Rust 开发环境搭建,鸿蒙基于 musl 库、强制二进制签名,无法直接使用通用 Linux 编译产物。需借助鸿蒙专属包管理器 Harmonybrew,提供两套编译方案:方案一安装 llvm-gcc-compat,零配置开箱即用;方案二仅安装 ohos-sdk,需手动配置 Cargo 链接器,二者都依托 ohos-sdk 完成自动签名编译。
完整的过程可以参考一下:
【OpenHarmony 鸿蒙 PC + CodeArts IDE 实现 Rust开发完整开发环境搭建指南】
一、rust_decimal 库介绍
作用
rust_decimal 是 Rust 高精度十进制浮点库,专门解决 f32/f64 二进制浮点数精度丢失问题(金钱、财务、计费、汇率、库存金额场景绝对不能用 f64)。
核心能力
- 高精度十进制运算,最多支持 28 位有效数字,金融标准精度;
- 支持加减乘除、四舍五入、向上/向下取整、保留指定位小数;
- 自带货币格式化、字符串互转、整数互相转换;
- 提供四则运算、比较、取模、幂运算;
- 支持 serde 序列化,可直接存 JSON、数据库金额字段。
适用场景
支付金额、订单总价、商品单价、税费计算、汇率换算、财务报表、记账系统、积分金额。
二进制浮点数坑对比
// f64 致命缺陷:0.1 + 0.2 != 0.3
println!("{}", 0.1 + 0.2); // 输出 0.30000000000000004
// rust_decimal 无精度丢失,0.1 + 0.2 严格等于 0.3
二、安装
cargo add rust_decimal --features std
cargo add rust_decimal_macros
Cargo.toml
[package]
name = "decimal_demo"
version = "0.1.0"
edition = "2021"
[dependencies]
rust_decimal = { version = "1.36", features = ["std"] }
rust_decimal_macros = "1.36"
rust_decimal_macros:提供dec!宏,快速字面量定义十进制数字

三、完整零报错示例代码 main.rs
use rust_decimal::prelude::*;
use rust_decimal_macros::dec;
fn main() {
println!("===== 1. 基础创建 Decimal 对象(宏/字符串/整数) =====");
demo_create_decimal();
println!("\n===== 2. 四则运算(无精度丢失) =====");
demo_calc();
println!("\n===== 3. 小数舍入、保留2位小数(金额场景) =====");
demo_round();
println!("\n===== 4. 简单金额格式化输出(人民币) =====");
demo_format_money();
println!("\n===== 5. 比较大小、取绝对值、取反 =====");
demo_compare_math();
println!("\n===== 6. 字符串、数字互相转换 =====");
demo_convert();
}
/// 1. 三种创建Decimal方式
fn demo_create_decimal() {
// 宏直接写小数,最常用
let price = dec!(99.99);
// 从整数构造
let num = Decimal::from(1000);
// 从安全字符串构造(前端传金额字符串推荐)
let from_str = Decimal::from_str("0.12345").unwrap();
println!("商品单价: {}", price);
println!("整数转换: {}", num);
println!("字符串构造: {}", from_str);
// 经典浮点坑对比
let a = dec!(0.1);
let b = dec!(0.2);
println!("0.1 + 0.2 = {}", a + b); // 精确等于0.3,无误差
}
/// 2. 加减乘除四则运算
fn demo_calc() {
let unit_price = dec!(29.99);
let count = dec!(3);
let tax_rate = dec!(0.09);
let subtotal = unit_price * count; // 小计
let tax = subtotal * tax_rate; // 税费
let total = subtotal + tax; // 总价
println!("单价{} × 数量{} = 小计{}", unit_price, count, subtotal);
println!("9%税费: {}", tax);
println!("订单总金额: {}", total);
// 除法
let total_money = dec!(99.9);
let people = dec!(3);
let avg = total_money / people;
println!("三人平分99.9,人均: {}", avg);
}
/// 3. 金额四舍五入、保留2位小数(修复RoundingStrategy不存在Ceiling/Floor)
fn demo_round() {
let raw = dec!(123.4567);
// 默认四舍五入保留2位小数(金融通用)
let round_2 = raw.round_dp(2);
// 向上取整(替代Ceiling)
let ceil_2 = raw.ceil();
// 向下取整(替代Floor)
let floor_2 = raw.floor();
println!("原始值: {}", raw);
println!("四舍五入保留2位: {}", round_2);
println!("整体向上取整: {}", ceil_2);
println!("整体向下取整: {}", floor_2);
}
/// 4. 简单货币格式化(移除FormatOptions不存在API)
fn demo_format_money() {
let money = dec!(1288.56);
println!("格式化金额: ¥{}", money.round_dp(2));
let small = dec!(0.05);
println!("小额金额: ¥{}", small.round_dp(2));
}
/// 5. 比较、绝对值、取反
fn demo_compare_math() {
let a = dec!(10.5);
let b = dec!(8.2);
let neg = dec!(-20.33);
println!("10.5 > 8.2 ? {}", a > b);
println!("相等判断: {}", dec!(5.0) == dec!(5));
println!("负数绝对值: {}", neg.abs());
println!("数字取反: {}", -a);
}
/// 6. 类型互相转换
fn demo_convert() {
let val = dec!(1234.56);
// Decimal → 字符串
let s = val.to_string();
println!("转字符串: {}", s);
// Decimal → i64(截断小数)
let int_part = val.trunc().to_i64().unwrap();
println!("取整数部分i64: {}", int_part);
// 字符串转回Decimal
let parse_back = Decimal::from_str(&s).unwrap();
println!("字符串转回Decimal: {}", parse_back);
}

一、库基础说明
rust_decimal 是 Rust 金融高精度十进制库,专门解决 f32/f64 二进制浮点精度丢失问题(0.1 + 0.2 ≠ 0.3),订单金额、支付、税费、记账场景必须使用,禁止用普通浮点数。
配套 rust_decimal_macros 提供 dec! 宏,快速创建十进制字面量。
头部导入
use rust_decimal::prelude::*;
use rust_decimal_macros::dec;
rust_decimal::prelude::*:预导入所有高频类型、方法、Trait(Decimal、运算、取整、转换方法),不用逐个单独导入。dec!宏:编译期生成高精度十进制数字,写法简洁,财务代码最常用。
main 入口函数
按业务常用场景分成6个模块依次执行:
- 创建Decimal数字的3种方式
- 加减乘除四则金融运算
- 小数保留、向上/向下取整
- 人民币金额打印格式化
- 大小比较、绝对值、负数取反
- Decimal 和字符串/整数互相转换
模块1 demo_create_decimal 创建数字
fn demo_create_decimal() {
// 宏直接写小数,最常用
let price = dec!(99.99);
// 从整数构造
let num = Decimal::from(1000);
// 从安全字符串构造(前端传金额字符串推荐)
let from_str = Decimal::from_str("0.12345").unwrap();
println!("商品单价: {}", price);
println!("整数转换: {}", num);
println!("字符串构造: {}", from_str);
// 经典浮点坑对比
let a = dec!(0.1);
let b = dec!(0.2);
println!("0.1 + 0.2 = {}", a + b); // 精确等于0.3,无误差
}
三种创建方式说明
dec!(99.99)宏
编译期直接解析数字生成Decimal,性能最高,写固定金额首选。Decimal::from(1000)
i32/i64 整数转金额,适合库存数量、整数元数。Decimal::from_str("0.12345")
前端接口、数据库取出的字符串金额专用。
前端如果传数字0.1用JSON浮点传输会失真,统一传字符串再解析是行业规范。
浮点缺陷演示
f64 二进制存储小数存在误差,0.1+0.2 结果是 0.30000000000000004;
Decimal 使用十进制存储,计算完全精准,金融不会出现分钱差错。
模块2 demo_calc 四则运算(订单计算场景)
fn demo_calc() {
let unit_price = dec!(29.99);
let count = dec!(3);
let tax_rate = dec!(0.09);
let subtotal = unit_price * count; // 小计
let tax = subtotal * tax_rate; // 税费
let total = subtotal + tax; // 总价
println!("单价{} × 数量{} = 小计{}", unit_price, count, subtotal);
println!("9%税费: {}", tax);
println!("订单总金额: {}", total);
// 除法
let total_money = dec!(99.9);
let people = dec!(3);
let avg = total_money / people;
println!("三人平分99.9,人均: {}", avg);
}
业务逻辑模拟
模拟电商订单计算:单价 × 数量 = 商品小计,小计 × 税率 = 税费,相加得到实付总价。
Decimal 重载 + - * / 运算符,写法和普通数字完全一致,全程无精度丢失。
除法适合分摊金额、单价换算。
模块3 demo_round 小数保留与取整(金融核心)
fn demo_round() {
let raw = dec!(123.4567);
// 默认四舍五入保留2位小数(金融通用)
let round_2 = raw.round_dp(2);
// 向上取整(替代Ceiling)
let ceil_2 = raw.ceil();
// 向下取整(替代Floor)
let floor_2 = raw.floor();
println!("原始值: {}", raw);
println!("四舍五入保留2位: {}", round_2);
println!("整体向上取整: {}", ceil_2);
println!("整体向下取整: {}", floor_2);
}
方法解释
.round_dp(n)
保留n位小数,默认四舍五入;人民币金额统一保留2位(元、分)。.ceil()向上取整
不管小数多少直接进1,商家计费、运费计算常用。.floor()向下取整
直接舍弃所有小数,退款、优惠抵扣场景使用。
模块4 demo_format_money 金额格式化打印
fn demo_format_money() {
let money = dec!(1288.56);
println!("格式化金额: ¥{}", money.round_dp(2));
let small = dec!(0.05);
println!("小额金额: ¥{}", small.round_dp(2));
}
逻辑简单:输出人民币符号 ¥,同时统一保留两位小数,保证页面展示格式统一,不会出现 1288.5 或 1288.567 这种不合规金额。
模块5 demo_compare_math 比较与基础数学运算
fn demo_compare_math() {
let a = dec!(10.5);
let b = dec!(8.2);
let neg = dec!(-20.33);
println!("10.5 > 8.2 ? {}", a > b);
println!("相等判断: {}", dec!(5.0) == dec!(5));
println!("负数绝对值: {}", neg.abs());
println!("数字取反: {}", -a);
}
- 支持
> < >= <= == !=全部比较运算符,用于判断满减门槛、余额是否充足; .abs()取绝对值,计算差价、手续费差额;-Decimal直接对数字取反,计算退款、负数余额。
模块6 demo_convert 类型互相转换
fn demo_convert() {
let val = dec!(1234.56);
// Decimal → 字符串
let s = val.to_string();
println!("转字符串: {}", s);
// Decimal → i64(截断小数)
let int_part = val.trunc().to_i64().unwrap();
println!("取整数部分i64: {}", int_part);
// 字符串转回Decimal
let parse_back = Decimal::from_str(&s).unwrap();
println!("字符串转回Decimal: {}", parse_back);
}
转换业务用途
.to_string():接口JSON返回金额、存入文本日志;.trunc().to_i64():舍弃小数只保留整数,用于把金额转为“分”存储到数据库整数字段;Decimal::from_str():后端接收前端字符串金额,反向解析。
全局开发规范总结
- 所有金额、汇率、计费数值禁止 f64/f32,统一 Decimal;
- 前后端传输金额优先字符串,规避浮点JSON失真;
- 展示、入库前一律用
.round_dp(2)保留两位小数; - 计算退款、运费时按需使用
ceil()/floor()明确取整规则,避免财务纠纷。
四、代码分段详细解释
1. 导入
use rust_decimal::prelude::*;
use rust_decimal_macros::dec;
prelude::*:一次性导入全部常用类型、Trait、枚举(Decimal、RoundingStrategy、FormatOptions等)dec!宏:编译期直接生成高精度十进制字面量,财务代码首选
2. 创建Decimal三种方式
dec!(123.45)宏:最简单,编译期求值,无运行时解析开销Decimal::from(i64/u64):整数转金额Decimal::from_str("0.123"):接收前端/数据库字符串金额,安全避免浮点误差
3. 四则运算核心优势
Decimal 重载 + - * / 运算符,运算全程十进制逻辑,不会出现二进制浮点微小误差,适合订单、税费、分润计算。
4. 舍入策略(金融必用)
.round_dp(n):默认四舍五入保留n位小数(人民币保留2位)RoundingStrategy::Ceiling:向上进位(商家计费常用)RoundingStrategy::Floor:向下舍弃(退款、扣减场景)
5. 货币格式化
FormatOptions 配置千位分隔符、小数点分隔符,输出 1,288.56 标准金额展示格式。
6. 类型互转
- 转字符串:接口返回、数据库存储
- 转整数:提取元、分整数存储
- 字符串反向解析:接收前端传入金额字符串
五、业务高频极简片段
1. 计算订单总价并保留2位小数
use rust_decimal::prelude::*;
use rust_decimal_macros::dec;
fn calc_total(price: Decimal, num: Decimal, tax: Decimal) -> Decimal {
let total = price * num * (dec!(1) + tax);
total.round_dp(2)
}
2. 安全解析前端金额字符串
fn parse_money(s: &str) -> Option<Decimal> {
Decimal::from_str(s).ok()
}
3. 金额比较判断优惠门槛
let order_total = dec!(299.9);
if order_total >= dec!(300) {
println!("满足满减条件");
}
六、重要使用规范
- 所有金额禁止 f32/f64,一律使用 Decimal;
- 前后端传金额优先用字符串传输,避免JSON浮点失真;
- 存储数据库建议 decimal 字段,或存整数“分”;
- 展示金额前统一
.round_dp(2)保留两位小数; - 涉及计费扣费明确指定舍入策略,避免纠纷。
更多推荐




所有评论(0)