Repository: jorgecarleitao/arrow2
Pull request #1116 (2022-06-28 10:11:23)

Renamed `f16::as_f32` to `f16::to_f32`
`as_` is for cheap conversions in Rust, and this is not a cheap conversion

codecov[bot]: # [Codecov](https://codecov.io/gh/jorgecarleitao/arrow2/pull/1116?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao) Report
> Merging [#1116](https://codecov.io/gh/jorgecarleitao/arrow2/pull/1116?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao) (4fc99ff) into [main](https://codecov.io/gh/jorgecarleitao/arrow2/commit/3b05377870ea63a63b369814d29b6fea0d584b17?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao) (3b05377) will **increase** coverage by `0.11%`.
> The diff coverage is `92.75%`.

```diff
@@            Coverage Diff             @@
##             main    #1116      +/-   ##
==========================================
+ Coverage   82.36%   82.47%   +0.11%     
==========================================
  Files         367      367              
  Lines       35382    35447      +65     
==========================================
+ Hits        29141    29235      +94     
+ Misses       6241     6212      -29     
```


| [Impacted Files](https://codecov.io/gh/jorgecarleitao/arrow2/pull/1116?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao) | Coverage Δ | |
|---|---|---|
| [src/types/native.rs](https://codecov.io/gh/jorgecarleitao/arrow2/pull/1116/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao#diff-c3JjL3R5cGVzL25hdGl2ZS5ycw==) | `86.28% <92.64%> (+18.35%)` | :arrow_up: |
| [src/compute/cast/primitive\_to.rs](https://codecov.io/gh/jorgecarleitao/arrow2/pull/1116/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao#diff-c3JjL2NvbXB1dGUvY2FzdC9wcmltaXRpdmVfdG8ucnM=) | `93.04% <100.00%> (ø)` | |
| [src/chunk.rs](https://codecov.io/gh/jorgecarleitao/arrow2/pull/1116/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao#diff-c3JjL2NodW5rLnJz) | `83.33% <0.00%> (-7.15%)` | :arrow_down: |
| [src/io/ipc/read/reader.rs](https://codecov.io/gh/jorgecarleitao/arrow2/pull/1116/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao#diff-c3JjL2lvL2lwYy9yZWFkL3JlYWRlci5ycw==) | `95.66% <0.00%> (-0.37%)` | :arrow_down: |
| [src/array/binary/mod.rs](https://codecov.io/gh/jorgecarleitao/arrow2/pull/1116/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao#diff-c3JjL2FycmF5L2JpbmFyeS9tb2QucnM=) | `90.12% <0.00%> (+1.23%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/jorgecarleitao/arrow2/pull/1116?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/jorgecarleitao/arrow2/pull/1116?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao). Last update [3b05377...4fc99ff](https://codecov.io/gh/jorgecarleitao/arrow2/pull/1116?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jorge+Leitao).


Patch:
diff --git a/src/array/fixed_size_binary/iterator.rs b/src/array/fixed_size_binary/iterator.rs
index aa60abfbfe..0e6c5c24c0 100644
--- a/src/array/fixed_size_binary/iterator.rs
+++ b/src/array/fixed_size_binary/iterator.rs
@@ -59,11 +59,11 @@ impl<'a> FixedSizeBinaryArray {
     pub fn iter(
         &'a self,
     ) -> ZipValidity<'a, &'a [u8], FixedSizeBinaryValuesIter<'a, FixedSizeBinaryArray>> {
-        zip_validity(self.iter_values(), self.validity.as_ref().map(|x| x.iter()))
+        zip_validity(self.values_iter(), self.validity.as_ref().map(|x| x.iter()))
     }
 
     /// Returns iterator over the values of [`FixedSizeBinaryArray`]
-    pub fn iter_values(&'a self) -> FixedSizeBinaryValuesIter<'a, FixedSizeBinaryArray> {
+    pub fn values_iter(&'a self) -> FixedSizeBinaryValuesIter<'a, FixedSizeBinaryArray> {
         FixedSizeBinaryValuesIter::new(self)
     }
 }
diff --git a/src/compute/cast/primitive_to.rs b/src/compute/cast/primitive_to.rs
index 9fdbe2f4eb..a36f16e3de 100644
--- a/src/compute/cast/primitive_to.rs
+++ b/src/compute/cast/primitive_to.rs
@@ -584,5 +584,5 @@ pub fn months_to_months_days_ns(from: &PrimitiveArray<i32>) -> PrimitiveArray<mo
 
 /// Casts f16 into f32
 pub fn f16_to_f32(from: &PrimitiveArray<f16>) -> PrimitiveArray<f32> {
-    unary(from, |x| x.as_f32(), DataType::Float32)
+    unary(from, |x| x.to_f32(), DataType::Float32)
 }
diff --git a/src/types/native.rs b/src/types/native.rs
index 4d4f34eb60..7e7711589b 100644
--- a/src/types/native.rs
+++ b/src/types/native.rs
@@ -301,6 +301,9 @@ impl PartialEq for f16 {
 
 // see https://github.com/starkat99/half-rs/blob/main/src/binary16.rs
 impl f16 {
+    /// The difference between 1.0 and the next largest representable number.
+    pub const EPSILON: f16 = f16(0x1400u16);
+
     #[inline]
     #[must_use]
     pub(crate) const fn is_nan(self) -> bool {
@@ -308,8 +311,7 @@ impl f16 {
     }
 
     /// Casts this `f16` to `f32`
-    #[inline]
-    pub fn as_f32(self) -> f32 {
+    pub fn to_f32(self) -> f32 {
         let i = self.0;
         // Check for signed zero
         if i & 0x7FFFu16 == 0 {
@@ -354,17 +356,76 @@ impl f16 {
         let man = (half_man & 0x03FFu32) << 13;
         f32::from_bits(sign | exp | man)
     }
+
+    /// Casts an `f32` into `f16`
+    pub fn from_f32(value: f32) -> Self {
+        let x: u32 = value.to_bits();
+
+        // Extract IEEE754 components
+        let sign = x & 0x8000_0000u32;
+        let exp = x & 0x7F80_0000u32;
+        let man = x & 0x007F_FFFFu32;
+
+        // Check for all exponent bits being set, which is Infinity or NaN
+        if exp == 0x7F80_0000u32 {
+            // Set mantissa MSB for NaN (and also keep shifted mantissa bits)
+            let nan_bit = if man == 0 { 0 } else { 0x0200u32 };
+            return f16(((sign >> 16) | 0x7C00u32 | nan_bit | (man >> 13)) as u16);
+        }
+
+        // The number is normalized, start assembling half precision version
+        let half_sign = sign >> 16;
+        // Unbias the exponent, then bias for half precision
+        let unbiased_exp = ((exp >> 23) as i32) - 127;
+        let half_exp = unbiased_exp + 15;
+
+        // Check for exponent overflow, return +infinity
+        if half_exp >= 0x1F {
+            return f16((half_sign | 0x7C00u32) as u16);
+        }
+
+        // Check for underflow
+        if half_exp <= 0 {
+            // Check mantissa for what we can do
+            if 14 - half_exp > 24 {
+                // No rounding possibility, so this is a full underflow, return signed zero
+                return f16(half_sign as u16);
+            }
+            // Don't forget about hidden leading mantissa bit when assembling mantissa
+            let man = man | 0x0080_0000u32;
+            let mut half_man = man >> (14 - half_exp);
+            // Check for rounding (see comment above functions)
+            let round_bit = 1 << (13 - half_exp);
+            if (man & round_bit) != 0 && (man & (3 * round_bit - 1)) != 0 {
+                half_man += 1;
+            }
+            // No exponent for subnormals
+            return f16((half_sign | half_man) as u16);
+        }
+
+        // Rebias the exponent
+        let half_exp = (half_exp as u32) << 10;
+        let half_man = man >> 13;
+        // Check for rounding (see comment above functions)
+        let round_bit = 0x0000_1000u32;
+        if (man & round_bit) != 0 && (man & (3 * round_bit - 1)) != 0 {
+            // Round it
+            f16(((half_sign | half_exp | half_man) + 1) as u16)
+        } else {
+            f16((half_sign | half_exp | half_man) as u16)
+        }
+    }
 }
 
 impl std::fmt::Debug for f16 {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{:?}", self.as_f32())
+        write!(f, "{:?}", self.to_f32())
     }
 }
 
 impl std::fmt::Display for f16 {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}", self.as_f32())
+        write!(f, "{}", self.to_f32())
     }
 }
 
@@ -386,3 +447,28 @@ impl NativeType for f16 {
         Self(u16::from_be_bytes(bytes))
     }
 }
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    #[test]
+    fn test_f16_to_f32() {
+        let f = f16::from_f32(7.0);
+        assert_eq!(f.to_f32(), 7.0f32);
+
+        // 7.1 is NOT exactly representable in 16-bit, it's rounded
+        let f = f16::from_f32(7.1);
+        let diff = (f.to_f32() - 7.1f32).abs();
+        // diff must be <= 4 * EPSILON, as 7 has two more significant bits than 1
+        assert!(diff <= 4.0 * f16::EPSILON.to_f32());
+
+        assert_eq!(f16(0x0000_0001).to_f32(), 2.0f32.powi(-24));
+        assert_eq!(f16(0x0000_0005).to_f32(), 5.0 * 2.0f32.powi(-24));
+
+        assert_eq!(f16(0x0000_0001), f16::from_f32(2.0f32.powi(-24)));
+        assert_eq!(f16(0x0000_0005), f16::from_f32(5.0 * 2.0f32.powi(-24)));
+
+        assert_eq!(format!("{}", f16::from_f32(7.0)), "7".to_string());
+        assert_eq!(format!("{:?}", f16::from_f32(7.0)), "7.0".to_string());
+    }
+}


Test patch:
diff --git a/tests/it/compute/arithmetics/decimal/div.rs b/tests/it/compute/arithmetics/decimal/div.rs
index 55cca8d303..39138d05dd 100644
--- a/tests/it/compute/arithmetics/decimal/div.rs
+++ b/tests/it/compute/arithmetics/decimal/div.rs
@@ -1,9 +1,12 @@
 #![allow(clippy::zero_prefixed_literal, clippy::inconsistent_digit_grouping)]
 
 use arrow2::array::*;
-use arrow2::compute::arithmetics::decimal::{adaptive_div, checked_div, div, saturating_div};
+use arrow2::compute::arithmetics::decimal::{
+    adaptive_div, checked_div, div, div_scalar, saturating_div,
+};
 use arrow2::compute::arithmetics::{ArrayCheckedDiv, ArrayDiv};
 use arrow2::datatypes::DataType;
+use arrow2::scalar::PrimitiveScalar;
 
 #[test]
 fn test_divide_normal() {
@@ -65,6 +68,28 @@ fn test_divide_panic() {
     div(&a, &b);
 }
 
+#[test]
+fn test_div_scalar() {
+    //   222.222 -->  222222000
+    //   123.456 -->     123456
+    // --------       ---------
+    //     1.800 <--       1800
+    let a = PrimitiveArray::from([Some(222_222i128), None]).to(DataType::Decimal(7, 3));
+    let b = PrimitiveScalar::from(Some(123_456i128)).to(DataType::Decimal(7, 3));
+    let result = div_scalar(&a, &b);
+
+    let expected = PrimitiveArray::from([Some(1_800i128), None]).to(DataType::Decimal(7, 3));
+    assert_eq!(result, expected);
+}
+
+#[test]
+#[should_panic(expected = "Overflow in multiplication presented for precision 5")]
+fn test_divide_scalar_panic() {
+    let a = PrimitiveArray::from([Some(99999i128)]).to(DataType::Decimal(5, 2));
+    let b = PrimitiveScalar::from(Some(000_01i128)).to(DataType::Decimal(5, 2));
+    div_scalar(&a, &b);
+}
+
 #[test]
 fn test_divide_saturating() {
     let a = PrimitiveArray::from([