This is an automated email from the ASF dual-hosted git repository.

Jefffrey pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new a4ac705350 feat(arrow-csv): add support for parsing `Float16` (#10343)
a4ac705350 is described below

commit a4ac70535046651d86e09811aabb93efe8d455d6
Author: Glatzel <[email protected]>
AuthorDate: Wed Jul 15 18:40:49 2026 +0800

    feat(arrow-csv): add support for parsing `Float16` (#10343)
    
    # Which issue does this PR close?
    
    - closes #10344
    
    # Rationale for this change
    
    `arrow-csv`'s CSV reader did not support parsing columns into
    `DataType::Float16`. When a schema explicitly declared a `Float16` field
    (schema inference never produces `Float16` on its own), parsing would
    fail with an "Unsupported data type" error since there was no
    corresponding match arm in the primitive-array builder dispatch. Since
    `Float16Type` is a standard Arrow primitive type like
    `Float32`/`Float64`, the CSV reader should be able to build arrays for
    it when given an explicit schema.
    
    # What changes are included in this PR?
    
    - Added a `DataType::Float16` match arm in `parse()`
    (`arrow-csv/src/reader/mod.rs`) that dispatches to
    `build_primitive_array::<Float16Type>`, matching the existing handling
    for `Float32`/`Float64`.
    - Added a `test_float_precision` test that exercises `Float16`,
    `Float32`, and `Float64` columns side by side against the same input
    values, verifying:
    - values that are exactly representable in binary (e.g. `1.5`, `0.25`,
    `-2.5`, `0`) round-trip correctly at all three precisions,
    - values with more precision than `f16`/`f32` can hold are correctly
    truncated at the appropriate width while `f64` retains full precision,
      - empty CSV fields decode to null across all three columns.
    
    # Are these changes tested?
    
    Yes, added a `test_float_precision` fn to test parsing csv with
    different precision `Float16`, `Float32`, `Float64`), including
    precision-loss and null handling.
    - `cargo test -p arrow-csv`
    
    # Are there any user-facing changes?
    
    Yes. Previously, attempting to read a CSV column into a schema field
    typed as `DataType::Float16` would fail with an `Parser error:
    Unsupported data type Float16`.
    
    This is now supported, consistent with the existing
    `Float32`/`Float64`handling. No existing behavior changes for other
    types.
---
 Cargo.lock                  |  1 +
 arrow-csv/Cargo.toml        |  1 +
 arrow-csv/src/reader/mod.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 66 insertions(+)

diff --git a/Cargo.lock b/Cargo.lock
index d781954e85..325baedf97 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -313,6 +313,7 @@ dependencies = [
  "csv",
  "csv-core",
  "futures",
+ "half",
  "regex",
  "tempfile",
  "tokio",
diff --git a/arrow-csv/Cargo.toml b/arrow-csv/Cargo.toml
index c44ec01ce3..689acf7b06 100644
--- a/arrow-csv/Cargo.toml
+++ b/arrow-csv/Cargo.toml
@@ -50,3 +50,4 @@ tempfile = "3.3"
 futures = "0.3"
 tokio = { version = "1.27", default-features = false, features = ["io-util"] }
 bytes = "1.4"
+half = { version = "2.1", default-features = false }
diff --git a/arrow-csv/src/reader/mod.rs b/arrow-csv/src/reader/mod.rs
index aae6f66cf5..51e855f7df 100644
--- a/arrow-csv/src/reader/mod.rs
+++ b/arrow-csv/src/reader/mod.rs
@@ -801,6 +801,9 @@ fn parse(
                 DataType::UInt64 => {
                     build_primitive_array::<UInt64Type>(line_number, rows, i, 
null_regex)
                 }
+                DataType::Float16 => {
+                    build_primitive_array::<Float16Type>(line_number, rows, i, 
null_regex)
+                }
                 DataType::Float32 => {
                     build_primitive_array::<Float32Type>(line_number, rows, i, 
null_regex)
                 }
@@ -2988,4 +2991,65 @@ mod tests {
         assert_eq!(c2.value(1), "something_cannot_be_inlined");
         assert_eq!(c2.value(2), "bar");
     }
+
+    #[test]
+    fn test_float_precision() {
+        let data = [
+            "f16,f32,f64",
+            "1.5,1.5,1.5",
+            "0.25,0.25,0.25",
+            "1.23456789,1.23456789,1.23456789",
+            "1.234567890123456,1.234567890123456,1.234567890123456",
+            "-2.5,-2.5,-2.5",
+            "0,0,0",
+            ",,",
+        ]
+        .join("\n");
+
+        let schema = Schema::new(vec![
+            Field::new("f16", DataType::Float16, true),
+            Field::new("f32", DataType::Float32, true),
+            Field::new("f64", DataType::Float64, true),
+        ]);
+
+        let mut reader = ReaderBuilder::new(Arc::new(schema))
+            .with_header(true)
+            .build(Cursor::new(data))
+            .unwrap();
+
+        let batch = reader.next().unwrap().unwrap();
+        assert_eq!(batch.num_rows(), 7);
+
+        let f16_col = batch.column(0).as_primitive::<Float16Type>();
+        let f32_col = batch.column(1).as_primitive::<Float32Type>();
+        let f64_col = batch.column(2).as_primitive::<Float64Type>();
+
+        assert_eq!(f16_col.value(0), half::f16::from_f32(1.5));
+        assert_eq!(f32_col.value(0), 1.5f32);
+        assert_eq!(f64_col.value(0), 1.5f64);
+
+        assert_eq!(f16_col.value(1), half::f16::from_f32(0.25));
+        assert_eq!(f32_col.value(1), 0.25f32);
+        assert_eq!(f64_col.value(1), 0.25f64);
+
+        assert_eq!(f16_col.value(2), half::f16::from_f32(1.234_567_9));
+        assert_eq!(f32_col.value(2), 1.234_567_9_f32);
+        assert_eq!(f64_col.value(2), 1.23456789f64);
+
+        assert_eq!(f16_col.value(3), 
half::f16::from_f64(1.234567890123456f64));
+        assert_eq!(f32_col.value(3), 1.234_567_9_f32);
+        assert_eq!(f64_col.value(3), 1.234567890123456f64);
+
+        assert_eq!(f16_col.value(4), half::f16::from_f32(-2.5));
+        assert_eq!(f32_col.value(4), -2.5f32);
+        assert_eq!(f64_col.value(4), -2.5f64);
+
+        assert_eq!(f16_col.value(5), half::f16::from_f32(0.0));
+        assert_eq!(f32_col.value(5), 0.0f32);
+        assert_eq!(f64_col.value(5), 0.0f64);
+
+        assert!(f16_col.is_null(6));
+        assert!(f32_col.is_null(6));
+        assert!(f64_col.is_null(6));
+    }
 }

Reply via email to