Hone logo
Hone
Problems

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:

  1. Initialization: The DecimalNumber class 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.
  2. Arithmetic Operations: Implement the __add__, __sub__, __mul__, and __truediv__ methods to support addition, subtraction, multiplication, and division respectively.
  3. 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).
  4. Representation: Implement __str__ and __repr__ for a clear string representation of the DecimalNumber object. The string representation should always show the exact number of decimal places specified, padding with zeros if necessary.
  5. Equality: Implement __eq__ to compare two DecimalNumber objects 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 DecimalNumber instance 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__. (The DecimalNumber class 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 decimal module as inspiration for how precision can be managed, but your implementation should be from scratch to demonstrate understanding. You can use decimal for 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, allowing eval(repr(obj)) == obj if possible.
Loading editor...
python