mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	The methods always return Decimal classes, even if they're
executed through a subclass (thanks Mark Dickinson). Added a bit of testing for this.
This commit is contained in:
		
							parent
							
								
									b67da23718
								
							
						
					
					
						commit
						6c398da0e7
					
				
					 2 changed files with 53 additions and 34 deletions
				
			
		|  | @ -1473,7 +1473,7 @@ def _fix_nan(self, context): | ||||||
|                 pos += 1 |                 pos += 1 | ||||||
|             payload = payload[pos:] |             payload = payload[pos:] | ||||||
|             return Decimal((self._sign, payload, self._exp)) |             return Decimal((self._sign, payload, self._exp)) | ||||||
|         return self |         return Decimal(self) | ||||||
| 
 | 
 | ||||||
|     def _fix(self, context): |     def _fix(self, context): | ||||||
|         """Round if it is necessary to keep self within prec precision. |         """Round if it is necessary to keep self within prec precision. | ||||||
|  | @ -1494,7 +1494,7 @@ def _fix(self, context): | ||||||
|                 return self._fix_nan(context) |                 return self._fix_nan(context) | ||||||
|             else: |             else: | ||||||
|                 # self is +/-Infinity; return unaltered |                 # self is +/-Infinity; return unaltered | ||||||
|                 return self |                 return Decimal(self) | ||||||
| 
 | 
 | ||||||
|         # if self is zero then exponent should be between Etiny and |         # if self is zero then exponent should be between Etiny and | ||||||
|         # Emax if _clamp==0, and between Etiny and Etop if _clamp==1. |         # Emax if _clamp==0, and between Etiny and Etop if _clamp==1. | ||||||
|  | @ -1507,7 +1507,7 @@ def _fix(self, context): | ||||||
|                 context._raise_error(Clamped) |                 context._raise_error(Clamped) | ||||||
|                 return Decimal((self._sign, (0,), new_exp)) |                 return Decimal((self._sign, (0,), new_exp)) | ||||||
|             else: |             else: | ||||||
|                 return self |                 return Decimal(self) | ||||||
| 
 | 
 | ||||||
|         # exp_min is the smallest allowable exponent of the result, |         # exp_min is the smallest allowable exponent of the result, | ||||||
|         # equal to max(self.adjusted()-context.prec+1, Etiny) |         # equal to max(self.adjusted()-context.prec+1, Etiny) | ||||||
|  | @ -1551,7 +1551,7 @@ def _fix(self, context): | ||||||
|             return Decimal((self._sign, self_padded, Etop)) |             return Decimal((self._sign, self_padded, Etop)) | ||||||
| 
 | 
 | ||||||
|         # here self was representable to begin with; return unchanged |         # here self was representable to begin with; return unchanged | ||||||
|         return self |         return Decimal(self) | ||||||
| 
 | 
 | ||||||
|     _pick_rounding_function = {} |     _pick_rounding_function = {} | ||||||
| 
 | 
 | ||||||
|  | @ -1678,10 +1678,10 @@ def _power_modulo(self, other, modulo, context=None): | ||||||
|                 return context._raise_error(InvalidOperation, 'sNaN', |                 return context._raise_error(InvalidOperation, 'sNaN', | ||||||
|                                         1, modulo) |                                         1, modulo) | ||||||
|             if self_is_nan: |             if self_is_nan: | ||||||
|                 return self |                 return self._fix_nan(context) | ||||||
|             if other_is_nan: |             if other_is_nan: | ||||||
|                 return other |                 return other._fix_nan(context) | ||||||
|             return modulo |             return modulo._fix_nan(context) | ||||||
| 
 | 
 | ||||||
|         # check inputs: we apply same restrictions as Python's pow() |         # check inputs: we apply same restrictions as Python's pow() | ||||||
|         if not (self._isinteger() and |         if not (self._isinteger() and | ||||||
|  | @ -2179,7 +2179,7 @@ def quantize(self, exp, rounding=None, context=None, watchexp=True): | ||||||
| 
 | 
 | ||||||
|             if exp._isinfinity() or self._isinfinity(): |             if exp._isinfinity() or self._isinfinity(): | ||||||
|                 if exp._isinfinity() and self._isinfinity(): |                 if exp._isinfinity() and self._isinfinity(): | ||||||
|                     return self  # if both are inf, it is OK |                     return Decimal(self)  # if both are inf, it is OK | ||||||
|                 return context._raise_error(InvalidOperation, |                 return context._raise_error(InvalidOperation, | ||||||
|                                         'quantize with one INF') |                                         'quantize with one INF') | ||||||
| 
 | 
 | ||||||
|  | @ -2254,7 +2254,7 @@ def _rescale(self, exp, rounding): | ||||||
|         rounding = rounding mode |         rounding = rounding mode | ||||||
|         """ |         """ | ||||||
|         if self._is_special: |         if self._is_special: | ||||||
|             return self |             return Decimal(self) | ||||||
|         if not self: |         if not self: | ||||||
|             return Decimal((self._sign, (0,), exp)) |             return Decimal((self._sign, (0,), exp)) | ||||||
| 
 | 
 | ||||||
|  | @ -2285,9 +2285,9 @@ def to_integral_exact(self, rounding=None, context=None): | ||||||
|             ans = self._check_nans(context=context) |             ans = self._check_nans(context=context) | ||||||
|             if ans: |             if ans: | ||||||
|                 return ans |                 return ans | ||||||
|             return self |             return Decimal(self) | ||||||
|         if self._exp >= 0: |         if self._exp >= 0: | ||||||
|             return self |             return Decimal(self) | ||||||
|         if not self: |         if not self: | ||||||
|             return Decimal((self._sign, (0,), 0)) |             return Decimal((self._sign, (0,), 0)) | ||||||
|         if context is None: |         if context is None: | ||||||
|  | @ -2310,9 +2310,9 @@ def to_integral_value(self, rounding=None, context=None): | ||||||
|             ans = self._check_nans(context=context) |             ans = self._check_nans(context=context) | ||||||
|             if ans: |             if ans: | ||||||
|                 return ans |                 return ans | ||||||
|             return self |             return Decimal(self) | ||||||
|         if self._exp >= 0: |         if self._exp >= 0: | ||||||
|             return self |             return Decimal(self) | ||||||
|         else: |         else: | ||||||
|             return self._rescale(0, rounding) |             return self._rescale(0, rounding) | ||||||
| 
 | 
 | ||||||
|  | @ -2426,6 +2426,9 @@ def max(self, other, context=None): | ||||||
|         """ |         """ | ||||||
|         other = _convert_other(other, raiseit=True) |         other = _convert_other(other, raiseit=True) | ||||||
| 
 | 
 | ||||||
|  |         if context is None: | ||||||
|  |             context = getcontext() | ||||||
|  | 
 | ||||||
|         if self._is_special or other._is_special: |         if self._is_special or other._is_special: | ||||||
|             # If one operand is a quiet NaN and the other is number, then the |             # If one operand is a quiet NaN and the other is number, then the | ||||||
|             # number is always returned |             # number is always returned | ||||||
|  | @ -2433,9 +2436,9 @@ def max(self, other, context=None): | ||||||
|             on = other._isnan() |             on = other._isnan() | ||||||
|             if sn or on: |             if sn or on: | ||||||
|                 if on == 1 and sn != 2: |                 if on == 1 and sn != 2: | ||||||
|                     return self |                     return self._fix_nan(context) | ||||||
|                 if sn == 1 and on != 2: |                 if sn == 1 and on != 2: | ||||||
|                     return other |                     return other._fix_nan(context) | ||||||
|                 return self._check_nans(other, context) |                 return self._check_nans(other, context) | ||||||
| 
 | 
 | ||||||
|         c = self.__cmp__(other) |         c = self.__cmp__(other) | ||||||
|  | @ -2455,8 +2458,6 @@ def max(self, other, context=None): | ||||||
|         else: |         else: | ||||||
|             ans = self |             ans = self | ||||||
| 
 | 
 | ||||||
|         if context is None: |  | ||||||
|             context = getcontext() |  | ||||||
|         if context._rounding_decision == ALWAYS_ROUND: |         if context._rounding_decision == ALWAYS_ROUND: | ||||||
|             return ans._fix(context) |             return ans._fix(context) | ||||||
|         return ans |         return ans | ||||||
|  | @ -2469,6 +2470,9 @@ def min(self, other, context=None): | ||||||
|         """ |         """ | ||||||
|         other = _convert_other(other, raiseit=True) |         other = _convert_other(other, raiseit=True) | ||||||
| 
 | 
 | ||||||
|  |         if context is None: | ||||||
|  |             context = getcontext() | ||||||
|  | 
 | ||||||
|         if self._is_special or other._is_special: |         if self._is_special or other._is_special: | ||||||
|             # If one operand is a quiet NaN and the other is number, then the |             # If one operand is a quiet NaN and the other is number, then the | ||||||
|             # number is always returned |             # number is always returned | ||||||
|  | @ -2476,9 +2480,9 @@ def min(self, other, context=None): | ||||||
|             on = other._isnan() |             on = other._isnan() | ||||||
|             if sn or on: |             if sn or on: | ||||||
|                 if on == 1 and sn != 2: |                 if on == 1 and sn != 2: | ||||||
|                     return self |                     return self._fix_nan(context) | ||||||
|                 if sn == 1 and on != 2: |                 if sn == 1 and on != 2: | ||||||
|                     return other |                     return other._fix_nan(context) | ||||||
|                 return self._check_nans(other, context) |                 return self._check_nans(other, context) | ||||||
| 
 | 
 | ||||||
|         c = self.__cmp__(other) |         c = self.__cmp__(other) | ||||||
|  | @ -2490,8 +2494,6 @@ def min(self, other, context=None): | ||||||
|         else: |         else: | ||||||
|             ans = other |             ans = other | ||||||
| 
 | 
 | ||||||
|         if context is None: |  | ||||||
|             context = getcontext() |  | ||||||
|         if context._rounding_decision == ALWAYS_ROUND: |         if context._rounding_decision == ALWAYS_ROUND: | ||||||
|             return ans._fix(context) |             return ans._fix(context) | ||||||
|         return ans |         return ans | ||||||
|  | @ -3087,6 +3089,9 @@ def max_mag(self, other, context=None): | ||||||
|         """Compares the values numerically with their sign ignored.""" |         """Compares the values numerically with their sign ignored.""" | ||||||
|         other = _convert_other(other, raiseit=True) |         other = _convert_other(other, raiseit=True) | ||||||
| 
 | 
 | ||||||
|  |         if context is None: | ||||||
|  |             context = getcontext() | ||||||
|  | 
 | ||||||
|         if self._is_special or other._is_special: |         if self._is_special or other._is_special: | ||||||
|             # If one operand is a quiet NaN and the other is number, then the |             # If one operand is a quiet NaN and the other is number, then the | ||||||
|             # number is always returned |             # number is always returned | ||||||
|  | @ -3094,9 +3099,9 @@ def max_mag(self, other, context=None): | ||||||
|             on = other._isnan() |             on = other._isnan() | ||||||
|             if sn or on: |             if sn or on: | ||||||
|                 if on == 1 and sn != 2: |                 if on == 1 and sn != 2: | ||||||
|                     return self |                     return self._fix_nan(context) | ||||||
|                 if sn == 1 and on != 2: |                 if sn == 1 and on != 2: | ||||||
|                     return other |                     return other._fix_nan(context) | ||||||
|                 return self._check_nans(other, context) |                 return self._check_nans(other, context) | ||||||
| 
 | 
 | ||||||
|         c = self.copy_abs().__cmp__(other.copy_abs()) |         c = self.copy_abs().__cmp__(other.copy_abs()) | ||||||
|  | @ -3108,8 +3113,6 @@ def max_mag(self, other, context=None): | ||||||
|         else: |         else: | ||||||
|             ans = self |             ans = self | ||||||
| 
 | 
 | ||||||
|         if context is None: |  | ||||||
|             context = getcontext() |  | ||||||
|         if context._rounding_decision == ALWAYS_ROUND: |         if context._rounding_decision == ALWAYS_ROUND: | ||||||
|             return ans._fix(context) |             return ans._fix(context) | ||||||
|         return ans |         return ans | ||||||
|  | @ -3118,6 +3121,9 @@ def min_mag(self, other, context=None): | ||||||
|         """Compares the values numerically with their sign ignored.""" |         """Compares the values numerically with their sign ignored.""" | ||||||
|         other = _convert_other(other, raiseit=True) |         other = _convert_other(other, raiseit=True) | ||||||
| 
 | 
 | ||||||
|  |         if context is None: | ||||||
|  |             context = getcontext() | ||||||
|  | 
 | ||||||
|         if self._is_special or other._is_special: |         if self._is_special or other._is_special: | ||||||
|             # If one operand is a quiet NaN and the other is number, then the |             # If one operand is a quiet NaN and the other is number, then the | ||||||
|             # number is always returned |             # number is always returned | ||||||
|  | @ -3125,9 +3131,9 @@ def min_mag(self, other, context=None): | ||||||
|             on = other._isnan() |             on = other._isnan() | ||||||
|             if sn or on: |             if sn or on: | ||||||
|                 if on == 1 and sn != 2: |                 if on == 1 and sn != 2: | ||||||
|                     return self |                     return self._fix_nan(context) | ||||||
|                 if sn == 1 and on != 2: |                 if sn == 1 and on != 2: | ||||||
|                     return other |                     return other._fix_nan(context) | ||||||
|                 return self._check_nans(other, context) |                 return self._check_nans(other, context) | ||||||
| 
 | 
 | ||||||
|         c = self.copy_abs().__cmp__(other.copy_abs()) |         c = self.copy_abs().__cmp__(other.copy_abs()) | ||||||
|  | @ -3139,8 +3145,6 @@ def min_mag(self, other, context=None): | ||||||
|         else: |         else: | ||||||
|             ans = other |             ans = other | ||||||
| 
 | 
 | ||||||
|         if context is None: |  | ||||||
|             context = getcontext() |  | ||||||
|         if context._rounding_decision == ALWAYS_ROUND: |         if context._rounding_decision == ALWAYS_ROUND: | ||||||
|             return ans._fix(context) |             return ans._fix(context) | ||||||
|         return ans |         return ans | ||||||
|  | @ -3296,7 +3300,7 @@ def rotate(self, other, context=None): | ||||||
|             return context._raise_error(InvalidOperation) |             return context._raise_error(InvalidOperation) | ||||||
| 
 | 
 | ||||||
|         if self._isinfinity(): |         if self._isinfinity(): | ||||||
|             return self |             return Decimal(self) | ||||||
| 
 | 
 | ||||||
|         # get values, pad if necessary |         # get values, pad if necessary | ||||||
|         torot = int(other) |         torot = int(other) | ||||||
|  | @ -3334,7 +3338,7 @@ def scaleb (self, other, context=None): | ||||||
|             return context._raise_error(InvalidOperation) |             return context._raise_error(InvalidOperation) | ||||||
| 
 | 
 | ||||||
|         if self._isinfinity(): |         if self._isinfinity(): | ||||||
|             return self |             return Decimal(self) | ||||||
| 
 | 
 | ||||||
|         d = Decimal((self._sign, self._int, self._exp + int(other))) |         d = Decimal((self._sign, self._int, self._exp + int(other))) | ||||||
|         d = d._fix(context) |         d = d._fix(context) | ||||||
|  | @ -3355,12 +3359,12 @@ def shift(self, other, context=None): | ||||||
|             return context._raise_error(InvalidOperation) |             return context._raise_error(InvalidOperation) | ||||||
| 
 | 
 | ||||||
|         if self._isinfinity(): |         if self._isinfinity(): | ||||||
|             return self |             return Decimal(self) | ||||||
| 
 | 
 | ||||||
|         # get values, pad if necessary |         # get values, pad if necessary | ||||||
|         torot = int(other) |         torot = int(other) | ||||||
|         if not torot: |         if not torot: | ||||||
|             return self |             return Decimal(self) | ||||||
|         rotdig = self._int |         rotdig = self._int | ||||||
|         topad = context.prec - len(rotdig) |         topad = context.prec - len(rotdig) | ||||||
|         if topad: |         if topad: | ||||||
|  | @ -3751,7 +3755,7 @@ def copy_decimal(self, a): | ||||||
|         >>> ExtendedContext.copy_decimal(Decimal('-1.00')) |         >>> ExtendedContext.copy_decimal(Decimal('-1.00')) | ||||||
|         Decimal("-1.00") |         Decimal("-1.00") | ||||||
|         """ |         """ | ||||||
|         return a |         return Decimal(a) | ||||||
| 
 | 
 | ||||||
|     def copy_negate(self, a): |     def copy_negate(self, a): | ||||||
|         """Returns a copy of the operand with the sign inverted. |         """Returns a copy of the operand with the sign inverted. | ||||||
|  |  | ||||||
|  | @ -1072,6 +1072,21 @@ def checkSameDec(operation, useOther=False): | ||||||
|         checkSameDec("to_eng_string") |         checkSameDec("to_eng_string") | ||||||
|         checkSameDec("to_integral") |         checkSameDec("to_integral") | ||||||
| 
 | 
 | ||||||
|  |     def test_subclassing(self): | ||||||
|  |         # Different behaviours when subclassing Decimal | ||||||
|  | 
 | ||||||
|  |         class MyDecimal(Decimal): | ||||||
|  |             pass | ||||||
|  | 
 | ||||||
|  |         d1 = MyDecimal(1) | ||||||
|  |         d2 = MyDecimal(2) | ||||||
|  |         d = d1 + d2 | ||||||
|  |         self.assertTrue(type(d) is Decimal) | ||||||
|  | 
 | ||||||
|  |         d = d1.max(d2) | ||||||
|  |         self.assertTrue(type(d) is Decimal) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class DecimalPythonAPItests(unittest.TestCase): | class DecimalPythonAPItests(unittest.TestCase): | ||||||
| 
 | 
 | ||||||
|     def test_pickle(self): |     def test_pickle(self): | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Facundo Batista
						Facundo Batista