简述
标准的 serde_json 序列化器不支持直接对浮点数进行格式化限制。如果将浮点数转换成字符串,又太low逼。这里重点推荐rust_decimal。
rust
#[derive(Serialize)]
pub struct StockTickRow {
datetime: NaiveDateTime,
code: String,
name: String,
#[serde(serialize_with = "serialize_float")]
weight: f32,
#[serde(serialize_with = "serialize_float")]
wnbz: f32,
#[serde(serialize_with = "serialize_float")]
chgp: f32,
}
fn serialize_float<S>(value: &f32, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// 这种方式不行,只能转换成字符串了
// let rounded_value = (value * 100.0).round() / 100.0;
// serializer.serialize_f32(rounded_value)
let formatted_value = format!("{:.2}", value); // 固定两位小数
serializer.serialize_str(&formatted_value)
}
上面是一开始我采用的方式,用了一天,心中不甘。就发现了rust_decimal。这个魔术师:
rust
#[derive(Serialize, Deserialize)]
pub struct StockTickRow {
datetime: NaiveDateTime,
code: String,
name: String,
#[serde(serialize_with = "serialize_decimal_two_digits")]
weight: Decimal,
#[serde(serialize_with = "serialize_decimal_two_digits")]
wnbz: Decimal,
}
fn serialize_decimal_two_digits<S>(value: &Decimal, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let rounded = value.round_dp(2);
let float_value: f64 = rounded.try_into().unwrap_or(0.0); // 尝试转换,失败时返回默认值
serializer.serialize_f64(float_value)
}
它能在结果中输出我们期望的格式:
javascript
{
"datetime": "2025-04-30T15:09:01",
"code": "603019",
"name": "中科曙光",
"weight": 1.3,
"wnbz": 1.22,
"chgp": 1.72,
}
如果你需要对Decimal类型进行某种转换或实现,这是个好的例子,它用在dolphindb数据库中:
javascript
impl FromDolphinScalar for Decimal {
fn from_scalar(scalar: &ScalarImpl) -> Option<Self> {
match scalar {
ScalarImpl::Float(c) => c.into_inner().map(|v| Decimal::from_f32(v).unwrap()),
ScalarImpl::Double(c) => c.into_inner().map(|v| Decimal::from_f64(v).unwrap()),
_ => None,
}
}
}
Rust-Decimal 除了输出json比较方便,它还是高精度金融计算库,不会在四舍五入时丢失精度 。更多信息自己查吧。