import time from decimal import Decimal, getcontext from difflib import SequenceMatcher from pint import UnitRegistry from rich.console import Console from rich.table import Table from rich.style import Style ureg = UnitRegistry() Quantity = ureg.Quantity class FormatedDecimal(Decimal): """Make sure we display the number in the fully expanded (not scientific) notation.""" def __str__(self): return f"{self:.{abs(self.as_tuple().exponent)}f}" # Example values str_a = "2.5489120456102" str_b = "83.21598756102002" str_c = "0.0002" str_d = "0.00000000000000000000000000000000965112" str_e = "745205937266232843946623988365475210078512674585246585" str_f = "63.1111111111111111111111111111111111101" str_g = "4522010410540451204102045263884101623.174125843864637484832326463289748" # Float version of examples float_a = float(str_a) float_b = float(str_b) float_c = float(str_c) float_d = float(str_d) float_e = float(str_e) float_f = float(str_f) float_g = float(str_g) # Decimal version of examples converted to Quantities quantity_a = Quantity(Decimal(str_a), "ft") quantity_b = Quantity(Decimal(str_b), "ft") quantity_c = Quantity(Decimal(str_c), "ft") quantity_d = Quantity(Decimal(str_d), "ft") quantity_e = Quantity(Decimal(str_e), "ft") quantity_f = Quantity(Decimal(str_f), "ft") quantity_g = Quantity(Decimal(str_g), "ft") # Iterables used in conversions precision_levels = [4, 5, 6, 8, 10, 15, 20, 25, 50, 75, 100, 125, 150, 200, 250] units = [ ("ft", "ft", "ft"), ("attometer", "exameter", "ft"), ("exameter", "attometer", "ft"), ] operations = [ {"function": add, "representation": "+"}, {"function": subtract, "representation": "-"}, {"function": multiply, "representation": "*"}, {"function": divide, "representation": "/"}, ] all_quantities = [quantity_a, quantity_b, quantity_c, quantity_d, quantity_e, quantity_f, quantity_g] combinations = [ { "strings": [str_a, str_b, str_c], "floats": [float_a, float_b, float_c], "quantities": [quantity_a, quantity_b, quantity_c], }, { "strings": [str_b, str_c, str_d], "floats": [float_b, float_c, float_d], "quantities": [quantity_b, quantity_c, quantity_d], }, { "strings": [str_c, str_d, str_e], "floats": [float_c, float_d, float_e], "quantities": [quantity_c, quantity_d, quantity_e], }, { "strings": [str_d, str_e, str_f], "floats": [float_d, float_e, float_f], "quantities": [quantity_d, quantity_e, quantity_f], }, { "strings": [str_e, str_f, str_g], "floats": [float_e, float_f, float_g], "quantities": [quantity_e, quantity_f, quantity_g], }, { "strings": [str_f, str_g, str_a], "floats": [float_f, float_g, float_a], "quantities": [quantity_f, quantity_g, quantity_a], }, { "strings": [str_g, str_a, str_b], "floats": [float_g, float_a, float_b], "quantities": [quantity_g, quantity_a, quantity_b], }, ] start = time.perf_counter() def add(x, y, z, precision_level=1): """Performs the addition operation with the three values, returning the table with the newly added row.""" getcontext().prec = precision_level operation_result = x + y + z return operation_result def subtract(x, y, z, precision_level=1): """Performs the subtraction operation with the three values, returning the table with the newly added row.""" getcontext().prec = precision_level operation_result = x - y - z return operation_result def multiply(x, y, z, precision_level=1): """Performs the multiplication operation with the three values, returning the table with the newly added row.""" getcontext().prec = precision_level operation_result = x * y * z return operation_result def divide(x, y, z, precision_level=1): """Performs the division operation with the three values, returning the table with the newly added row.""" getcontext().prec = precision_level operation_result = x / y / z return operation_result def print_quantities_at_each_precision(): """Prints the quantities at each precision level.""" console = Console(record=True) for quantity in all_quantities: print("\n\n") table = Table(title="Checking precision of original values") columns = ["Prec", "Value 1"] for column in columns: table.add_column(column) # Temporarily set to max precision to get the length of the fully expanded number getcontext().prec = precision_levels[-1] max_precision_value = str(FormatedDecimal(quantity.magnitude.normalize())).rstrip("0").rstrip(".") for precision_level in precision_levels: getcontext().prec = precision_level formatted_value = str(FormatedDecimal(quantity.magnitude.normalize())).rstrip("0").rstrip(".") table.add_row( str(precision_level), formatted_value, style=Style(color="green") if formatted_value == max_precision_value else Style(color="red"), ) console.print(table) # print(console.export_html()) def print_examples_with_various_operations(): """Prints various combinations or the example values using different operations and precision levels.""" console = Console(record=True) for operation in operations: for combination in combinations: print("\n\n") table = Table( title=( f"{operation['function'].__name__}: {combination['strings'][0]} {operation['representation']} " f"{combination['strings'][1]} {operation['representation']} {combination['strings'][2]}" ) ) columns = ["Type", "Prec", "Result"] for column in columns: table.add_column(column) # Get float value formatted_float_value = str( FormatedDecimal(operation["function"](*combination["floats"], precision_level=1)) ) table.add_row("float", "N/A", formatted_float_value, style=Style(color="orange3")) # Get value at highest precision max_precision_value = operation["function"]( *combination["quantities"], precision_level=precision_levels[-1] ) formatted_max_precision_value = ( str(FormatedDecimal(max_precision_value.magnitude.normalize())).rstrip("0").rstrip(".") ) # Compare float with max value, and add a row showing where they differ s = SequenceMatcher(None, formatted_float_value, formatted_max_precision_value) matching_blocks = s.get_matching_blocks() if len(matching_blocks) > 0: match_length = matching_blocks[0].size table.add_row( "(match)", "N/A", f"{'-' * (match_length)}{'x' * (len(formatted_float_value) - match_length)}", style=Style(color="orange3"), ) for precision_level in precision_levels: quantity_value = operation["function"](*combination["quantities"], precision_level) formatted_quantity_value = ( str(FormatedDecimal(quantity_value.magnitude.normalize())).rstrip("0").rstrip(".") ) table.add_row( "Quantity", str(precision_level), formatted_quantity_value, style=( Style(color="green") if formatted_quantity_value == formatted_max_precision_value else Style(color="red") ), ) console.print(table) # print(console.export_html()) print_quantities_at_each_precision() print("\n\n") print_examples_with_various_operations() end = time.perf_counter() print(f"Time taken: {end - start:.2f} seconds")