Decimal Precision Calculator
In many real-world applications, especially in finance and scientific computing, standard floating-point arithmetic in Python can lead to subtle inaccuracies due to how numbers are represented. This challenge asks you to implement a system that can perform arithmetic operations with guaranteed decimal precision, avoiding these common pitfalls.
Problem Description
You need to create a class, let's call it DecimalNumber, that represents numbers with a specified decimal precision. This class should allow for basic arithmetic operations (addition, subtraction, multiplication, and division) and ensure that the results maintain the defined precision.
Key Requirements:
- Initialization: The
DecimalNumberclass should be initialized with a numerical value (which can be an integer, float, or string) and an integer representing the desired number of decimal places. - Arithmetic Operations: Implement the
__add__,__sub__,__mul__, and__truediv__methods to support addition, subtraction, multiplication, and division respectively. - Precision Maintenance: All arithmetic operations must produce results that are rounded to the specified number of decimal places. Use standard rounding rules (round half to even, or round half up if explicitly preferred, though the former is generally more standard for financial applications).
- Representation: Implement
__str__and__repr__for a clear string representation of theDecimalNumberobject. The string representation should always show the exact number of decimal places specified, padding with zeros if necessary. - Equality: Implement
__eq__to compare twoDecimalNumberobjects for equality.
Expected Behavior:
- When performing operations, the intermediate calculations should be handled in a way that preserves precision as much as possible before final rounding.
- Division should handle cases where the result would have more decimal places than specified by truncating or rounding appropriately.
- The class should be robust enough to handle various input types for initialization.
Edge Cases:
- Zero: Operations involving zero.
- Negative Numbers: Operations with negative values.
- Division by Zero: This should raise a
ZeroDivisionError. - Input Types: Handling integer, float, and string inputs during initialization.
- Precision Changes: The precision is fixed upon initialization. Subsequent operations should maintain that precision.
Examples
Example 1:
num1 = DecimalNumber(10.2345, 2)
num2 = DecimalNumber(5.6789, 2)
result_add = num1 + num2
print(result_add)
print(repr(result_add))
Output:
15.91
DecimalNumber(15.91, 2)
Explanation: 10.2345 + 5.6789 = 15.9134. Rounded to 2 decimal places, this becomes 15.91.
Example 2:
num1 = DecimalNumber("123.45", 3)
num2 = DecimalNumber(2.3, 3)
result_mul = num1 * num2
print(result_mul)
print(repr(result_mul))
Output:
283.935
DecimalNumber(283.935, 3)
Explanation: 123.45 * 2.3 = 283.935. Already at 3 decimal places, no rounding needed.
Example 3:
num1 = DecimalNumber(100, 4)
num2 = DecimalNumber(3, 4)
result_div = num1 / num2
print(result_div)
print(repr(result_div))
num3 = DecimalNumber(10, 2)
num4 = DecimalNumber(3, 2)
result_div_complex = num3 / num4
print(result_div_complex)
print(repr(result_div_complex))
Output:
33.3333
DecimalNumber(33.3333, 4)
10.00 / 3.00 = 3.333333... Rounded to 2 decimal places, this becomes 3.33
3.33
DecimalNumber(3.33, 2)
Explanation:
For 100 / 3: The exact result is 33.333333... Rounded to 4 decimal places, it's 33.3333.
For 10 / 3: The exact result is 3.333333... Rounded to 2 decimal places, it's 3.33.
Example 4 (Edge Case - String Input and Padding):
num1 = DecimalNumber("5", 3)
num2 = DecimalNumber(0.5, 3)
result = num1 - num2
print(result)
print(repr(result))
Output:
4.500
DecimalNumber(4.500, 3)
Explanation: 5 - 0.5 = 4.5. Padded to 3 decimal places, it becomes 4.500.
Constraints
- The number of decimal places for a
DecimalNumberinstance will be between 0 and 10, inclusive. - Input values for initialization will be integers, floats, or strings representing valid numbers.
- The magnitude of numbers will fit within standard Python integer and float limits.
- For division, the divisor will not be zero during the calculation of
__truediv__. (TheDecimalNumberclass itself should handle explicit division by zero). - Performance should be reasonable for typical arithmetic operations; extreme micro-optimization is not the primary goal, but the solution should not be excessively slow.
Notes
- Consider using Python's built-in
decimalmodule as inspiration for how precision can be managed, but your implementation should be from scratch to demonstrate understanding. You can usedecimalfor internal calculations if you wish, but the core logic of handling and formatting should be yours. - Think about how you will store the number internally to maintain precision. Representing it as an integer and scaling it might be a good approach.
- Pay close attention to the rounding behavior. Python's
round()function for floats uses round half to even by default. - The
__repr__method should ideally be unambiguous, allowingeval(repr(obj)) == objif possible.