|
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") |