Test float-to-decimal conversion against exactly generated values.
(float_ty, decimal_traits)
| 2273 | @pytest.mark.parametrize('decimal_traits', decimal_type_traits, |
| 2274 | ids=lambda v: v.name) |
| 2275 | def test_cast_float_to_decimal_random(float_ty, decimal_traits): |
| 2276 | """ |
| 2277 | Test float-to-decimal conversion against exactly generated values. |
| 2278 | """ |
| 2279 | r = random.Random(43) |
| 2280 | np_float_ty = { |
| 2281 | pa.float32(): np.float32, |
| 2282 | pa.float64(): np.float64, |
| 2283 | }[float_ty] |
| 2284 | mantissa_bits = { |
| 2285 | pa.float32(): 24, |
| 2286 | pa.float64(): 53, |
| 2287 | }[float_ty] |
| 2288 | float_exp_min, float_exp_max = { |
| 2289 | pa.float32(): (-126, 127), |
| 2290 | pa.float64(): (-1022, 1023), |
| 2291 | }[float_ty] |
| 2292 | mantissa_digits = math.floor(math.log10(2**mantissa_bits)) |
| 2293 | max_precision = decimal_traits.max_precision |
| 2294 | |
| 2295 | # For example, decimal32 <-> float64 |
| 2296 | if max_precision < mantissa_digits: |
| 2297 | mantissa_bits = math.floor(math.log2(10**max_precision)) |
| 2298 | mantissa_digits = math.floor(math.log10(2**mantissa_bits)) |
| 2299 | |
| 2300 | with decimal.localcontext() as ctx: |
| 2301 | precision = mantissa_digits |
| 2302 | ctx.prec = precision |
| 2303 | # The scale must be chosen so as |
| 2304 | # 1) it's within bounds for the decimal type |
| 2305 | # 2) the floating point exponent is within bounds |
| 2306 | min_scale = max(-max_precision, |
| 2307 | precision + math.ceil(math.log10(2**float_exp_min))) |
| 2308 | max_scale = min(max_precision, |
| 2309 | math.floor(math.log10(2**float_exp_max))) |
| 2310 | for scale in range(min_scale, max_scale): |
| 2311 | decimal_ty = decimal_traits.factory(precision, scale) |
| 2312 | # We want to random-generate a float from its mantissa bits |
| 2313 | # and exponent, and compute the expected value in the |
| 2314 | # decimal domain. The float exponent has to ensure the |
| 2315 | # expected value doesn't overflow and doesn't lose precision. |
| 2316 | float_exp = (-mantissa_bits + |
| 2317 | math.floor(math.log2(10**(precision - scale)))) |
| 2318 | assert float_exp_min <= float_exp <= float_exp_max |
| 2319 | for i in range(5): |
| 2320 | mantissa = r.randrange(0, 2**mantissa_bits) |
| 2321 | float_val = np.ldexp(np_float_ty(mantissa), float_exp) |
| 2322 | assert isinstance(float_val, np_float_ty) |
| 2323 | # Make sure we compute the exact expected value and |
| 2324 | # round by half-to-even when converting to the expected precision. |
| 2325 | if float_exp >= 0: |
| 2326 | expected = decimal.Decimal(mantissa) * 2**float_exp |
| 2327 | else: |
| 2328 | expected = decimal.Decimal(mantissa) / 2**-float_exp |
| 2329 | expected_as_int = round(expected.scaleb(scale)) |
| 2330 | actual = pc.cast( |
| 2331 | pa.scalar(float_val, type=float_ty), decimal_ty).as_py() |
| 2332 | actual_as_int = round(actual.scaleb(scale)) |