|
| 1 | +"""Uncertainty rounding carry-over (audit finding F5). |
| 2 | +
|
| 3 | +When a magnitude>=1 uncertainty rounds UP to the next power of ten at the |
| 4 | +requested significant digits, the carry handling must keep the magnitude |
| 5 | +(e.g. 9.6 @ 1 sig fig -> "10", not "1"), not shrink it 10x. |
| 6 | +""" |
| 7 | + |
| 8 | +from __future__ import annotations |
| 9 | + |
| 10 | +from mpmath import mp |
| 11 | + |
| 12 | +from datalab_latex.latex_formatting import _uncertainty_decimal_places |
| 13 | + |
| 14 | + |
| 15 | +def test_carry_to_next_power_of_ten_keeps_magnitude_one_sig_fig(): |
| 16 | + # 9.6 to 1 significant digit rounds to 10, not 1. |
| 17 | + decimal_places, unc_str = _uncertainty_decimal_places(mp.mpf("9.6"), 1) |
| 18 | + assert (decimal_places, unc_str) == (0, "10") |
| 19 | + |
| 20 | + |
| 21 | +def test_carry_to_next_power_of_ten_keeps_magnitude_two_sig_figs(): |
| 22 | + # 99.6 to 2 significant digits rounds to 100, not 10. |
| 23 | + decimal_places, unc_str = _uncertainty_decimal_places(mp.mpf("99.6"), 2) |
| 24 | + assert (decimal_places, unc_str) == (0, "100") |
| 25 | + |
| 26 | + |
| 27 | +def test_non_carry_cases_unchanged(): |
| 28 | + # Regression guard: values that do NOT carry stay exactly as before. |
| 29 | + assert _uncertainty_decimal_places(mp.mpf("9.6"), 2) == (1, "96") |
| 30 | + assert _uncertainty_decimal_places(mp.mpf("1.23"), 1) == (0, "1") |
| 31 | + assert _uncertainty_decimal_places(mp.mpf("0.045"), 1) == (2, "4") |
| 32 | + |
| 33 | + |
| 34 | +def test_carry_with_fractional_uncertainty_keeps_magnitude(): |
| 35 | + # 0.96 to 1 sig fig carries to 1.0: unc_int "10" at decimal_places 1 |
| 36 | + # (real value = 10 * 10**-1 = 1.0), NOT "1" at dp 1 (= 0.1, 10x too small). |
| 37 | + decimal_places, unc_str = _uncertainty_decimal_places(mp.mpf("0.96"), 1) |
| 38 | + assert unc_str == "10" |
| 39 | + assert decimal_places == 1 |
| 40 | + assert int(unc_str) * mp.power(10, -decimal_places) == mp.mpf("1") |
0 commit comments