Skip to content

Commit

Permalink
Boavizta#214 add rounding based on min and max
Browse files Browse the repository at this point in the history
This function should be used when the API returns a value with an
associated min and max value. The rounding depends on the delta
betwwen min and max
  • Loading branch information
PierreRust committed Sep 15, 2023
1 parent 743be68 commit 85ef420
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 0 deletions.
34 changes: 34 additions & 0 deletions boaviztapi/utils/roundit.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,39 @@ def min_significant_figures(*inputs):
return sigfig if sigfig > config['min_significant_figures'] else config['min_significant_figures']


def round_based_on_min_max(val, min_val, max_val, precision):
"""
Returns a rounded values for `val`.
The rounding depends on the difference betwwen `min_val` and `max-val`.
The bigger the difference, the more agressive the rounding.
The precision is a % used to round the value.
"""
if precision == 0:
raise ValueError("Invalid precision value, cannot be 0%")
if max_val < min_val:
raise ValueError("round_based_on_min_max : min must be less than max")

# value for approx% of the min max delta
approx = (max_val - min_val) / (100/precision)
if approx == 0:
return val
significant = math.floor(math.log10(approx))

# Mathematically, these two calculation should be equivalent, but
# dividing by very small number and multiplying by very large number
# cause issues with floats.
# We avoid these issues by inverting operation based on the sign of significant
if significant > 0:
rounded = round(val / 10 ** significant) * 10**significant
else:
rounded = round(val / 10 ** significant) / 10**-significant

return rounded


def round_to_sigfig(x, significant_figures):
"""
returns a float rounded to significant figures
Expand Down Expand Up @@ -70,6 +103,7 @@ def remove_unsignificant_zeros(x):
exponent = -math.log10(divider) + 1
return float(x * (Decimal(10 ** exponent)))


def to_precision(x, p):
"""
Util function
Expand Down
47 changes: 47 additions & 0 deletions tests/unit/test_roundit.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import boaviztapi.utils.roundit as rd
import pytest


def test_sigfig_():
Expand Down Expand Up @@ -33,3 +34,49 @@ def test_round_to_sigfig():
assert rd.round_to_sigfig(0.0251, 3) == 0.0251
# this case is not supported as the return float value is 0.2
# assert rd.round_to_sigfig(0.202, 2) == '0.20'


def test_round_based_on_min_max():

# high difference between min and max:
assert rd.round_based_on_min_max(20.29217, 10.91891, 81.81527, 10) == 20
# small difference between min and max:
assert rd.round_based_on_min_max(20.29217, 18.91891, 22.81527, 10) == 20.3

# Different precision:
# 10%
assert rd.round_based_on_min_max(1.2345, 1.2, 1.3, 10) == 1.23
# 1%
assert rd.round_based_on_min_max(1.2345, 1.2, 1.3, 1) == 1.234

# WHen no rounding is requied :
assert rd.round_based_on_min_max(60, 10, 90, 10) == 60
assert rd.round_based_on_min_max(60, 10, 90, 1) == 60

# Various other tests:
assert rd.round_based_on_min_max(1.52366871, 1, 2, 1) == 1.52
assert rd.round_based_on_min_max(1.52366871, 1, 2, 10) == 1.5

assert rd.round_based_on_min_max(20.29217, 10.91891, 81.81527, 10) == 20
assert rd.round_based_on_min_max(
1819.821672, 110.14710120000001, 5419.285269084, 10) == 1800
assert rd.round_based_on_min_max(
0.02040328338, 0.020400523740000003, 0.02042139678, 10) == 0.020403
assert rd.round_based_on_min_max(
0.00030760589391948, 0.63406418256e-05, 0.00127183984353, 10) == 0.0003
assert rd.round_based_on_min_max(
306.0165, 179.92950000000002, 1133.6115, 10) == 310
assert rd.round_based_on_min_max(
61648.853641199996, 62.2570572, 2241972.40986, 10) == 100000


def test_round_based_on_min_max_corner_cases():

# min == max : must retun the original value without any rounding
assert rd.round_based_on_min_max(1, 1, 1, 1) == 1
# precision == 0 : must raise ValueError
with pytest.raises(ValueError) as e:
rd.round_based_on_min_max(1, 1, 1, 0)
# min > max
with pytest.raises(ValueError) as e:
rd.round_based_on_min_max(5, 10, 5, 10)

0 comments on commit 85ef420

Please sign in to comment.