You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by li...@apache.org on 2026/07/09 09:13:21 UTC
(iceberg-rust) branch main updated: fix(spec): support float/double conversion in Datum::to() (#2783)
This is an automated email from the ASF dual-hosted git repository.
blackmwk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-rust.git
The following commit(s) were added to refs/heads/main by this push:
new ff678ca6d fix(spec): support float/double conversion in Datum::to() (#2783)
ff678ca6d is described below
commit ff678ca6d1123d9d82902b34379ccba2caf172b6
Author: Matt Butrovich <mb...@users.noreply.github.com>
AuthorDate: Thu Jul 9 05:13:15 2026 -0400
fix(spec): support float/double conversion in Datum::to() (#2783)
## Which issue does this PR close?
- Closes #.
## What changes are included in this PR?
`Datum::to()` had no conversion between `float` and `double`, so binding
a predicate whose literal type differed from the column type failed with
`Can't convert datum from double type to float type`. This is common
because engines often carry a floating point literal as a double even
when the column is a float.
This PR adds the two missing arms:
- `Double -> Float`: narrows through a new `f64_to_f32` helper that
returns `AboveMax` / `BelowMin` when the value is outside the finite
float range, matching the existing integer narrowing helpers
(`i64_to_i32`, `i128_to_i64`). A plain `as f32` cast would saturate to
`+/-inf` and change comparison results, so the sentinels let predicate
binding resolve the comparison correctly.
- `Float -> Double`: widens losslessly.
## Are these changes tested?
Yes. Four unit tests in `spec/values/tests.rs` cover the in range
narrowing, the above max and below min cases, and the widening. The
`AboveMax` / `BelowMin` handling is already exercised by the existing
predicate binding tests in `expr::predicate`.
---------
Co-authored-by: emkornfield <em...@gmail.com>
Co-authored-by: blackmwk <li...@outlook.com>
---
crates/iceberg/src/spec/values/datum.rs | 16 ++++++++++++
crates/iceberg/src/spec/values/tests.rs | 44 +++++++++++++++++++++++++++++++++
2 files changed, 60 insertions(+)
diff --git a/crates/iceberg/src/spec/values/datum.rs b/crates/iceberg/src/spec/values/datum.rs
index 2040d4634..9e7b9a19e 100644
--- a/crates/iceberg/src/spec/values/datum.rs
+++ b/crates/iceberg/src/spec/values/datum.rs
@@ -1086,6 +1086,16 @@ impl Datum {
}
}
+ fn f64_to_f32(val: f64) -> Datum {
+ if val > f32::MAX as f64 {
+ Datum::new(PrimitiveType::Float, PrimitiveLiteral::AboveMax)
+ } else if val < (f32::MIN as f64) {
+ Datum::new(PrimitiveType::Float, PrimitiveLiteral::BelowMin)
+ } else {
+ Datum::float(val as f32)
+ }
+ }
+
fn string_to_i128<S: AsRef<str>>(s: S) -> Result<i128> {
s.as_ref().parse::<i128>().map_err(|e| {
Error::new(ErrorKind::DataInvalid, "Can't parse string to i128.").with_source(e)
@@ -1123,6 +1133,12 @@ impl Datum {
(PrimitiveLiteral::Long(val), _, PrimitiveType::Int) => {
Ok(Datum::i64_to_i32(*val))
}
+ (PrimitiveLiteral::Double(val), _, PrimitiveType::Float) => {
+ Ok(Datum::f64_to_f32(val.0))
+ }
+ (PrimitiveLiteral::Float(val), _, PrimitiveType::Double) => {
+ Ok(Datum::double(val.0 as f64))
+ }
(PrimitiveLiteral::Long(val), _, PrimitiveType::Timestamp) => {
Ok(Datum::timestamp_micros(*val))
}
diff --git a/crates/iceberg/src/spec/values/tests.rs b/crates/iceberg/src/spec/values/tests.rs
index bc8b011fd..a8a6716f9 100644
--- a/crates/iceberg/src/spec/values/tests.rs
+++ b/crates/iceberg/src/spec/values/tests.rs
@@ -1183,6 +1183,50 @@ fn test_datum_long_convert_to_int_below_min() {
assert_eq!(result, expected);
}
+#[test]
+fn test_datum_double_convert_to_float() {
+ let datum = Datum::double(2.5);
+
+ let result = datum.to(&Primitive(PrimitiveType::Float)).unwrap();
+
+ let expected = Datum::float(2.5);
+
+ assert_eq!(result, expected);
+}
+
+#[test]
+fn test_datum_double_convert_to_float_above_max() {
+ let datum = Datum::double(1e39);
+
+ let result = datum.to(&Primitive(PrimitiveType::Float)).unwrap();
+
+ let expected = Datum::new(PrimitiveType::Float, PrimitiveLiteral::AboveMax);
+
+ assert_eq!(result, expected);
+}
+
+#[test]
+fn test_datum_double_convert_to_float_below_min() {
+ let datum = Datum::double(-1e39);
+
+ let result = datum.to(&Primitive(PrimitiveType::Float)).unwrap();
+
+ let expected = Datum::new(PrimitiveType::Float, PrimitiveLiteral::BelowMin);
+
+ assert_eq!(result, expected);
+}
+
+#[test]
+fn test_datum_float_convert_to_double() {
+ let datum = Datum::float(2.5);
+
+ let result = datum.to(&Primitive(PrimitiveType::Double)).unwrap();
+
+ let expected = Datum::double(2.5);
+
+ assert_eq!(result, expected);
+}
+
#[test]
fn test_datum_long_convert_to_timestamp() {
let datum = Datum::long(12345);