Test float-to-decimal conversion against exactly generated values.
(float_ty, decimal_traits)
| 2419 | @pytest.mark.parametrize('decimal_traits', decimal_type_traits, |
| 2420 | ids=lambda v: v.name) |
| 2421 | def test_cast_float_to_decimal_random(float_ty, decimal_traits): |
| 2422 | """ |
| 2423 | Test float-to-decimal conversion against exactly generated values. |
| 2424 | """ |
| 2425 | r = random.Random(43) |
| 2426 | np_float_ty = { |
| 2427 | pa.float32(): np.float32, |
| 2428 | pa.float64(): np.float64, |
| 2429 | }[float_ty] |
| 2430 | mantissa_bits = { |
| 2431 | pa.float32(): 24, |
| 2432 | pa.float64(): 53, |
| 2433 | }[float_ty] |
| 2434 | float_exp_min, float_exp_max = { |
| 2435 | pa.float32(): (-126, 127), |
| 2436 | pa.float64(): (-1022, 1023), |
| 2437 | }[float_ty] |
| 2438 | mantissa_digits = math.floor(math.log10(2**mantissa_bits)) |
| 2439 | max_precision = decimal_traits.max_precision |
| 2440 | |
| 2441 | # For example, decimal32 <-> float64 |
| 2442 | if max_precision < mantissa_digits: |
| 2443 | mantissa_bits = math.floor(math.log2(10**max_precision)) |
| 2444 | mantissa_digits = math.floor(math.log10(2**mantissa_bits)) |
| 2445 | |
| 2446 | with decimal.localcontext() as ctx: |
| 2447 | precision = mantissa_digits |
| 2448 | ctx.prec = precision |
| 2449 | # The scale must be chosen so as |
| 2450 | # 1) it's within bounds for the decimal type |
| 2451 | # 2) the floating point exponent is within bounds |
| 2452 | min_scale = max(-max_precision, |
| 2453 | precision + math.ceil(math.log10(2**float_exp_min))) |
| 2454 | max_scale = min(max_precision, |
| 2455 | math.floor(math.log10(2**float_exp_max))) |
| 2456 | for scale in range(min_scale, max_scale): |
| 2457 | decimal_ty = decimal_traits.factory(precision, scale) |
| 2458 | # We want to random-generate a float from its mantissa bits |
| 2459 | # and exponent, and compute the expected value in the |
| 2460 | # decimal domain. The float exponent has to ensure the |
| 2461 | # expected value doesn't overflow and doesn't lose precision. |
| 2462 | float_exp = (-mantissa_bits + |
| 2463 | math.floor(math.log2(10**(precision - scale)))) |
| 2464 | assert float_exp_min <= float_exp <= float_exp_max |
| 2465 | for i in range(5): |
| 2466 | mantissa = r.randrange(0, 2**mantissa_bits) |
| 2467 | float_val = np.ldexp(np_float_ty(mantissa), float_exp) |
| 2468 | assert isinstance(float_val, np_float_ty) |
| 2469 | # Make sure we compute the exact expected value and |
| 2470 | # round by half-to-even when converting to the expected precision. |
| 2471 | if float_exp >= 0: |
| 2472 | expected = decimal.Decimal(mantissa) * 2**float_exp |
| 2473 | else: |
| 2474 | expected = decimal.Decimal(mantissa) / 2**-float_exp |
| 2475 | expected_as_int = round(expected.scaleb(scale)) |
| 2476 | actual = pc.cast( |
| 2477 | pa.scalar(float_val, type=float_ty), decimal_ty).as_py() |
| 2478 | actual_as_int = round(actual.scaleb(scale)) |