|
5 | 5 | import pytest |
6 | 6 |
|
7 | 7 | import freshdata as fd |
| 8 | +from freshdata.steps.dtypes import _finalize_numeric |
8 | 9 |
|
9 | 10 |
|
10 | 11 | def clean1(values, **options): |
@@ -156,3 +157,43 @@ def test_date_objects_normalized_to_datetime64(): |
156 | 157 | def test_huge_integers_stay_float_not_overflow(huge): |
157 | 158 | s = clean1(huge) |
158 | 159 | assert s.dtype == "float64" |
| 160 | + |
| 161 | + |
| 162 | +def test_finalize_numeric_int64_boundaries_no_overflow_no_demotion(): |
| 163 | + """Regression for #34: exact int64 boundary handling. |
| 164 | +
|
| 165 | + float64 cannot represent 2**63 - 1; it rounds up to 2**63, so any |
| 166 | + float-space threshold either rejects legitimate values or admits an |
| 167 | + overflowing one. The largest float64 below 2**63 is 2**63 - 1024 and |
| 168 | + must convert exactly; float(2**63) must stay float64, never wrap. |
| 169 | + """ |
| 170 | + top = float(2**63 - 1024) |
| 171 | + out = _finalize_numeric(pd.Series([top, 1.0])) |
| 172 | + assert str(out.dtype) == "int64" |
| 173 | + assert int(out.iloc[0]) == 2**63 - 1024 |
| 174 | + |
| 175 | + out = _finalize_numeric(pd.Series([float(2**63), 1.0])) |
| 176 | + assert str(out.dtype) == "float64" |
| 177 | + |
| 178 | + out = _finalize_numeric(pd.Series([top, None])) |
| 179 | + assert str(out.dtype) == "Int64" |
| 180 | + assert int(out.iloc[0]) == 2**63 - 1024 |
| 181 | + |
| 182 | + |
| 183 | +def test_finalize_numeric_int64_min_not_rejected_by_abs_asymmetry(): |
| 184 | + """int64's range is asymmetric: -2**63 is representable, +2**63 is not. |
| 185 | + A magnitude-only guard rejected the legitimate minimum.""" |
| 186 | + bottom = float(-(2**63)) |
| 187 | + out = _finalize_numeric(pd.Series([bottom, 0.0])) |
| 188 | + assert str(out.dtype) == "int64" |
| 189 | + assert int(out.iloc[0]) == -(2**63) |
| 190 | + |
| 191 | + out = _finalize_numeric(pd.Series([float(-(2**64)), 0.0])) |
| 192 | + assert str(out.dtype) == "float64" |
| 193 | + |
| 194 | + |
| 195 | +def test_huge_integer_strings_still_stay_float_end_to_end(): |
| 196 | + """Pipeline-level guard: values beyond int64 parse to float64, exactly |
| 197 | + as before the boundary fix.""" |
| 198 | + s = clean1(["18446744073709551616", "1"]) # 2**64 |
| 199 | + assert s.dtype == "float64" |
0 commit comments