如何正确使用FixedU128获取JSON中的0.5价格值?
链下Worker中用lite-json获取浮点数值的正确转换方法
问题场景
我在链下Worker中通过lite-json解析如下JSON数据:
{ "price": 0.5 }
实现代码如下:
let price_str = match obj.clone() { JsonValue::Object(obj_data) => { let (_, v) = obj_data .into_iter() .find(|(k, _)| k.iter().copied().eq("price".chars()))?; match v { JsonValue::Number(val) => val, _ => return None, } } _ => return None, }; let bits = price_str.integer as u128 * 10u128.pow(price_str.exponent as u32) + u128::from(price_str.fraction); log::info!("bits: {}", bits); // Adjust the denominator based on the desired scale let fixed_value = FixedU128::saturating_from_rational(bits, 10u128.pow(price_str.fraction_length as u32)); log::info!("value of integer is {}", fixed_value);
得到日志:
2023-12-09 22:25:03.084 INFO offchain-worker pallet_template: bits: 5 2023-12-09 22:25:03.084 INFO offchain-worker pallet_template: value of integer is 500000000000000000
期望fixed_value为0.5,但实际得到500000000000000000,尝试多种转换方法结果不是0就是这个大数,求正确获取价格的方式。
解决方案
核心原因说明
FixedU128默认采用18位小数精度(即基于1e18的缩放因子),所以输出的500000000000000000本质是0.5的18位精度固定点表示(0.5 × 1e18 = 5e17),这是固定点类型的正常存储逻辑,并非错误。
正确处理方式
如果需要得到可读的浮点数值或调整精度,可按以下方式处理:
- 转换为浮点数(适合显示/非精确计算场景)
// 将FixedU128转为f64类型 let float_price = fixed_value.to_float(); log::info!("price as float: {}", float_price); // 输出0.5
- 自定义固定点精度
如果不需要18位精度,比如仅需2位小数,可重新计算缩放比例:
// 定义目标小数位数 let desired_decimals = 2; let scaled_value = FixedU128::saturating_from_rational(bits, 10u128.pow(desired_decimals)); // 转换为带小数的字符串输出 log::info!("scaled price: {}", scaled_value.to_string()); // 输出0.50
- 简化lite-json解析逻辑
原解析代码可简化,避免不必要的克隆和遍历:
// 直接通过key查找数值,无需遍历对象 let price_num = match obj { JsonValue::Object(obj_data) => obj_data.get("price") .and_then(|v| match v { JsonValue::Number(num) => Some(num), _ => None, })?, _ => return None, };
关键注意点
- 固定点类型(如
FixedU128)的设计目的是通过整数存储实现精确计算,直接打印会输出放大后的整数,这是特性而非bug。 - 链上/链下Worker中推荐用固定点类型做精确计算,浮点数仅适合显示场景,避免精度损失。
内容的提问来源于stack exchange,提问作者Deepak Chaudhary
相关产品推荐
相关产品推荐

