fix(tdengine): preserve FLOAT decimal precision

Closes #6937
This commit is contained in:
t8y2 committed 2026-08-22 01:07:45 +00:00
1 parent da49e03048
commit b015cfeef5
2 files changed
+50 -1

No files matched your search

+46 -1
View File
@@ -18,7 +18,7 @@ pub fn borrowed_value_to_json(value: BorrowedValue<'_>, timezone: Option<Tz>) ->
BorrowedValue::USmallInt(value) => json!(value),
BorrowedValue::UInt(value) => json!(value),
BorrowedValue::UBigInt(value) => json!(value),
BorrowedValue::Float(value) => finite_number(value as f64),
BorrowedValue::Float(value) => finite_float_number(value),
BorrowedValue::Double(value) => finite_number(value),
BorrowedValue::VarChar(value) => Value::String(value.to_string()),
BorrowedValue::NChar(value) => Value::String(value.into_owned()),
@@ -38,6 +38,17 @@ fn finite_number(value: f64) -> Value {
Number::from_f64(value).map(Value::Number).unwrap_or_else(|| Value::String(value.to_string()))
}
fn finite_float_number(value: f32) -> Value {
let shortest = value.to_string();
if value.is_finite() {
// Widening f32 directly exposes binary approximation digits that were never significant in FLOAT.
let rounded = shortest.parse::<f64>().expect("a formatted finite f32 is a valid f64");
Value::Number(Number::from_f64(rounded).expect("a finite f32 is a finite f64"))
} else {
Value::String(shortest)
}
}
fn binary_json(value: &[u8]) -> Value {
json!({ "$binary": STANDARD.encode(value) })
}
@@ -86,6 +97,40 @@ mod tests {
use super::*;
use taos::Ty;
#[test]
fn serializes_float_with_shortest_round_trippable_decimal() {
let cases = [
(8.6_f32, "8.6"),
(0.72_f32, "0.72"),
(59.6_f32, "59.6"),
(-13.25_f32, "-13.25"),
(0.0_f32, "0.0"),
(-0.0_f32, "-0.0"),
(f32::from_bits(1), "1e-45"),
(f32::MIN_POSITIVE, "1.1754944e-38"),
(f32::MAX, "3.4028235e+38"),
(1.0e-7_f32, "1e-7"),
(1.0e20_f32, "1e+20"),
];
for (value, expected) in cases {
let encoded = borrowed_value_to_json(BorrowedValue::Float(value), None);
assert!(encoded.is_number(), "finite FLOAT must remain a JSON number");
assert_eq!(encoded.to_string(), expected);
assert_eq!((encoded.as_f64().unwrap() as f32).to_bits(), value.to_bits());
}
}
#[test]
fn keeps_float_fallback_and_other_numeric_types_unchanged() {
assert_eq!(borrowed_value_to_json(BorrowedValue::Float(f32::NAN), None), json!("NaN"));
assert_eq!(borrowed_value_to_json(BorrowedValue::Float(f32::INFINITY), None), json!("inf"));
assert_eq!(borrowed_value_to_json(BorrowedValue::Float(f32::NEG_INFINITY), None), json!("-inf"));
assert_eq!(borrowed_value_to_json(BorrowedValue::Null(Ty::Float), None), Value::Null);
assert_eq!(borrowed_value_to_json(BorrowedValue::Double(8.600000381469727), None), json!(8.600000381469727));
assert_eq!(borrowed_value_to_json(BorrowedValue::Int(42), None), json!(42));
}
#[test]
fn preserves_timestamp_precision_with_at_least_milliseconds() {
assert_eq!(