summaryrefslogtreecommitdiff
path: root/modules/language/python/module/decimal.scm
diff options
context:
space:
mode:
Diffstat (limited to 'modules/language/python/module/decimal.scm')
-rw-r--r--modules/language/python/module/decimal.scm2002
1 files changed, 1078 insertions, 924 deletions
diff --git a/modules/language/python/module/decimal.scm b/modules/language/python/module/decimal.scm
index a4c31c7..5ec8295 100644
--- a/modules/language/python/module/decimal.scm
+++ b/modules/language/python/module/decimal.scm
@@ -2984,792 +2984,906 @@
(if (= (modulo n 5) 0)
(set! n (+ n 1))))
- ans = _dec_from_triple(0, str(n), e)
-
- # round, and fit to current context
- context = context._shallow_copy()
- rounding = context._set_rounding(ROUND_HALF_EVEN)
- ans = ans._fix(context)
- context.rounding = rounding
+ (let ((ans (_dec_from_triple 0 (str n) e))
+ ;; round, and fit to current context
+ (context ((ref context '_shallow_copy)))
+ (rounding ((ref context '_set_rounding) ROUND_HALF_EVEN))
+ (ans ((ref ans '_fix) context)))
+ (set context 'rounding rounding)
+ ans))))))
+
+ (define max
+ (lam (self other (= context None))
+ "Returns the larger value.
- return ans
+ Like max(self, other) except if one is not a number, returns
+ NaN (and signals if one is sNaN). Also rounds.
+ "
+ (twix
+ (let ((other (_convert_other other #:raiseit #t))))
+ (let (get-context context))
+ ((if (or (ref self '_is_special) (ref other '_is_special))
+ (begin
+ ;; If one operand is a quiet NaN and the other is number, then
+ ;; the number is always returned
+ (let ((sn ((ref self '_isnan)))
+ (on ((ref other '_isnan))))
+ (if (or (bool sn) (bool on))
+ (if (and (= on 1) (= sn 0))
+ ((ref self '_fix) context)
+ (if (and (= sn 1) (= on 0))
+ ((ref other '_fix) context)
+ ((ref self '_check_nans) other context)))
+ #f)))
+ #f) it it)
- def max(self, other, context=None):
- """Returns the larger value.
+ (let ((c ((ref self '_cmp) other)))
+ (if (= c 0)
+ ;; If both operands are finite and equal in numerical value
+ ;; then an ordering is applied:
+ ;;
+ ;; If the signs differ then max returns the operand with the
+ ;; positive sign and min returns the operand with the negative
+ ;; sign
+ ;;
+ ;; If the signs are the same then the exponent is used to select
+ ;; the result. This is exactly the ordering used in
+ ;; compare_total.
+ (set! c ((ref self 'compare_total) other)))
+
+ (let ((ans (if (= c -1)
+ other
+ self)))
+
+ ((ref ans '_fix) context))))))
+
+ (define min
+ (lam (self other (= context None))
+ "Returns the larger value.
Like max(self, other) except if one is not a number, returns
NaN (and signals if one is sNaN). Also rounds.
- """
- other = _convert_other(other, raiseit=True)
-
- if context is None:
- context = getcontext()
-
- if self._is_special or other._is_special:
- # If one operand is a quiet NaN and the other is number, then the
- # number is always returned
- sn = self._isnan()
- on = other._isnan()
- if sn or on:
- if on == 1 and sn == 0:
- return self._fix(context)
- if sn == 1 and on == 0:
- return other._fix(context)
- return self._check_nans(other, context)
-
- c = self._cmp(other)
- if c == 0:
- # If both operands are finite and equal in numerical value
- # then an ordering is applied:
- #
- # If the signs differ then max returns the operand with the
- # positive sign and min returns the operand with the negative sign
- #
- # If the signs are the same then the exponent is used to select
- # the result. This is exactly the ordering used in compare_total.
- c = self.compare_total(other)
-
- if c == -1:
- ans = other
- else:
- ans = self
+ "
+ (twix
+ (let ((other (_convert_other other #:raiseit #t))))
+ (let (get-context context))
+ ((if (or (ref self '_is_special) (ref other '_is_special))
+ (begin
+ ;; If one operand is a quiet NaN and the other is number, then
+ ;; the number is always returned
+ (let ((sn ((ref self '_isnan)))
+ (on ((ref other '_isnan))))
+ (if (or (bool sn) (bool on))
+ (if (and (= on 1) (= sn 0))
+ ((ref self '_fix) context)
+ (if (and (= sn 1) (= on 0))
+ ((ref other '_fix) context)
+ ((ref self '_check_nans) other context)))
+ #f)))
+ #f) it it)
- return ans._fix(context)
+ (let ((c ((ref self '_cmp) other)))
+ (if (= c 0)
+ ;; If both operands are finite and equal in numerical value
+ ;; then an ordering is applied:
+ ;;
+ ;; If the signs differ then max returns the operand with the
+ ;; positive sign and min returns the operand with the negative
+ ;; sign
+ ;;
+ ;; If the signs are the same then the exponent is used to select
+ ;; the result. This is exactly the ordering used in
+ ;; compare_total.
+ (set! c ((ref self 'compare_total) other)))
+
+ (let ((ans (if (= c -1)
+ self
+ other)))
+
+ ((ref ans '_fix) context))))))
+
+ (define _isinteger
+ (lambda (self)
+ "Returns whether self is an integer"
+ (cond
+ ((bool (ref self '_is_special))
+ #f)
+ ((>= (ref self '_exp) 0)
+ #t)
+ (else
+ (let ((rest (pylist-ref (ref self '_int) (ref self '_exp))))
+ (equal? rest "0"*(len rest)))))))
- def min(self, other, context=None):
- """Returns the smaller value.
+ (define _iseven
+ (lambda (self)
+ "Returns True if self is even. Assumes self is an integer."
+ (if (or (not (bool self)) (> (ref self '_exp) 0))
+ #t
+ (in (pylist-ref (ref self '_int) (+ -1 (ref self '_exp)))
+ "02468"))))
- Like min(self, other) except if one is not a number, returns
- NaN (and signals if one is sNaN). Also rounds.
- """
- other = _convert_other(other, raiseit=True)
-
- if context is None:
- context = getcontext()
-
- if self._is_special or other._is_special:
- # If one operand is a quiet NaN and the other is number, then the
- # number is always returned
- sn = self._isnan()
- on = other._isnan()
- if sn or on:
- if on == 1 and sn == 0:
- return self._fix(context)
- if sn == 1 and on == 0:
- return other._fix(context)
- return self._check_nans(other, context)
-
- c = self._cmp(other)
- if c == 0:
- c = self.compare_total(other)
-
- if c == -1:
- ans = self
- else:
- ans = other
-
- return ans._fix(context)
-
- def _isinteger(self):
- """Returns whether self is an integer"""
- if self._is_special:
- return False
- if self._exp >= 0:
- return True
- rest = self._int[self._exp:]
- return rest == '0'*len(rest)
-
- def _iseven(self):
- """Returns True if self is even. Assumes self is an integer."""
- if not self or self._exp > 0:
- return True
- return self._int[-1+self._exp] in '02468'
-
- def adjusted(self):
- """Return the adjusted exponent of self"""
- try:
- return self._exp + len(self._int) - 1
- # If NaN or Infinity, self._exp is string
- except TypeError:
- return 0
+ (define adjusted
+ (lambda (self)
+ "Return the adjusted exponent of self"
+ (try
+ (lambda () (+ (ref self '_exp) + (len (ref self '_int)) -1))
+ ;; If NaN or Infinity, self._exp is string
+ (#:except TypeError (lambda z 0)))))
- def canonical(self):
- """Returns the same Decimal object.
+ (define canonical
+ (lambda (self)
+ "Returns the same Decimal object.
As we do not have different encodings for the same number, the
received object already is in its canonical form.
- """
- return self
+ "
+ self))
- def compare_signal(self, other, context=None):
- """Compares self to the other operand numerically.
+ (define compare_signal
+ (lam (self other (= context None))
+ "Compares self to the other operand numerically.
It's pretty much like compare(), but all NaNs signal, with signaling
NaNs taking precedence over quiet NaNs.
- """
- other = _convert_other(other, raiseit = True)
- ans = self._compare_check_nans(other, context)
- if ans:
- return ans
- return self.compare(other, context=context)
+ "
+ (let* ((other (_convert_other other #raiseit #t))
+ (ans ((ref self '_compare_check_nans) other context)))
+ (if (bool ans)
+ and
+ ((ref self 'compare) other #:context context)))))
- def compare_total(self, other, context=None):
- """Compares self to other using the abstract representations.
+ (define compare_total
+ (lam (self other (= context None))
+ "Compares self to other using the abstract representations.
This is not like the standard compare, which use their numerical
value. Note that a total ordering is defined for all possible abstract
representations.
- """
- other = _convert_other(other, raiseit=True)
-
- # if one is negative and the other is positive, it's easy
- if self._sign and not other._sign:
- return _NegativeOne
- if not self._sign and other._sign:
- return _One
- sign = self._sign
-
- # let's handle both NaN types
- self_nan = self._isnan()
- other_nan = other._isnan()
- if self_nan or other_nan:
- if self_nan == other_nan:
- # compare payloads as though they're integers
- self_key = len(self._int), self._int
- other_key = len(other._int), other._int
- if self_key < other_key:
- if sign:
- return _One
- else:
- return _NegativeOne
- if self_key > other_key:
- if sign:
- return _NegativeOne
- else:
- return _One
- return _Zero
-
- if sign:
- if self_nan == 1:
- return _NegativeOne
- if other_nan == 1:
- return _One
- if self_nan == 2:
- return _NegativeOne
- if other_nan == 2:
- return _One
- else:
- if self_nan == 1:
- return _One
- if other_nan == 1:
- return _NegativeOne
- if self_nan == 2:
- return _One
- if other_nan == 2:
- return _NegativeOne
-
- if self < other:
- return _NegativeOne
- if self > other:
- return _One
-
- if self._exp < other._exp:
- if sign:
- return _One
- else:
- return _NegativeOne
- if self._exp > other._exp:
- if sign:
- return _NegativeOne
- else:
- return _One
- return _Zero
-
-
- def compare_total_mag(self, other, context=None):
- """Compares self to other using abstract repr., ignoring sign.
+ "
+
+ (twix
+ (let ((other (_convert_other other #raiseit #t))))
+
+ ;; if one is negative and the other is positive, it's easy
+ ((and (bool (ref self '_sign)) (not (bool other '_sign))) it
+ _NegativeOne)
+
+ ((and (not (bool (ref self '_sign))) (bool other '_sign)) it
+ One)
+
+ (let ((sign (ref self '_sign))
+ ;; let's handle both NaN types
+ (self_nan ((ref self '_isnan)))
+ (other_nan ((ref other '_isnan)))))
+
+ ((if (or (bool self_nan) (bool other_nan))
+ (if (= self_nan other_nan)
+ ;; compare payloads as though they're integers
+ (let ((self_key (list (len (ref self '_int))
+ (ref self '_int)))
+ (other_key (list (len (ref other '_int))
+ (ref other '_int))))
+ (cond
+ ((< self_key other_key)
+ (if (bool sign)
+ _One
+ _NegativeOne)
+ ((> self_key other_key)
+ (if sign
+ _NegativeOne
+ _One))
+ (else
+ _Zero))))
+
+ (if (bool sign)
+ (cond
+ ((= self_nan 1)
+ _NegativeOne)
+ ((= other_nan 1)
+ _One)
+ ((= self_nan 2)
+ _NegativeOne)
+ ((= other_nan 2)
+ _One)
+ (else
+ #f))
+ (cond
+ ((= self_nan 1)
+ _One)
+ ((= other_nan 1)
+ _NegativeOne)
+ ((= self_nan 2)
+ _One)
+ ((= other_nan 2)
+ _NegativeOne)
+ (else #f))))
+ #f) it it)
+
+ ((< self other) it
+ _NegativeOne)
+
+ ((> self > other) it
+ _One)
+
+ ((< (ref self '_exp) (ref other '_exp)) it
+ (if (bool sign)
+ _One
+ _NegativeOne))
+
+ ((> (ref self '_exp) (ref other '_exp)) it
+ (if (bool sign)
+ _NegativeOne
+ _One))
+
+ _Zero)))
+
+
+ (define compare_total_mag
+ (lam (self other (= context None))
+ "Compares self to other using abstract repr., ignoring sign.
Like compare_total, but with operand's sign ignored and assumed to be 0.
- """
- other = _convert_other(other, raiseit=True)
+ "
+ (let* ((other (_convert_other other #:raiseit #t))
+ (s ((ref self 'copy_abs)))
+ (o ((ref other 'copy_abs))))
+ ((ref s 'compare_total) o))))
- s = self.copy_abs()
- o = other.copy_abs()
- return s.compare_total(o)
+ (define copy_abs
+ (lambda (self)
+ "Returns a copy with the sign set to 0. "
+ (_dec_from_triple 0 (ref self '_int)
+ (ref self '_exp) (ref self '_is_special))))
- def copy_abs(self):
- """Returns a copy with the sign set to 0. """
- return _dec_from_triple(0, self._int, self._exp, self._is_special)
+ (define copy_negate
+ (lambda (self)
+ "Returns a copy with the sign inverted."
+ (if (bool (ref self '_sign))
+ (_dec_from_triple 0 (ref self '_int)
+ (ref self '_exp) (ref self '_is_special))
+ (_dec_from_triple 1 (ref self '_int)
+ (ref self '_exp) (ref self '_is_special)))))
+
+ (define copy_sign
+ (lam (self other (= context None))
+ "Returns self with the sign of other."
+ (let ((other (_convert_other other #:raiseit #t)))
+ (_dec_from_triple (ref other 'sign) (ref self '_int)
+ (ref self '_exp) (ref self '_is_special)))))
+
+ (define exp
+ (lam (self (= context None))
+ "Returns e ** self."
- def copy_negate(self):
- """Returns a copy with the sign inverted."""
- if self._sign:
- return _dec_from_triple(0, self._int, self._exp, self._is_special)
- else:
- return _dec_from_triple(1, self._int, self._exp, self._is_special)
-
- def copy_sign(self, other, context=None):
- """Returns self with the sign of other."""
- other = _convert_other(other, raiseit=True)
- return _dec_from_triple(other._sign, self._int,
- self._exp, self._is_special)
-
- def exp(self, context=None):
- """Returns e ** self."""
-
- if context is None:
- context = getcontext()
-
- # exp(NaN) = NaN
- ans = self._check_nans(context=context)
- if ans:
- return ans
-
- # exp(-Infinity) = 0
- if self._isinfinity() == -1:
- return _Zero
-
- # exp(0) = 1
- if not self:
- return _One
-
- # exp(Infinity) = Infinity
- if self._isinfinity() == 1:
- return Decimal(self)
-
- # the result is now guaranteed to be inexact (the true
- # mathematical result is transcendental). There's no need to
- # raise Rounded and Inexact here---they'll always be raised as
- # a result of the call to _fix.
- p = context.prec
- adj = self.adjusted()
-
- # we only need to do any computation for quite a small range
- # of adjusted exponents---for example, -29 <= adj <= 10 for
- # the default context. For smaller exponent the result is
- # indistinguishable from 1 at the given precision, while for
- # larger exponent the result either overflows or underflows.
- if self._sign == 0 and adj > len(str((context.Emax+1)*3)):
- # overflow
- ans = _dec_from_triple(0, '1', context.Emax+1)
- elif self._sign == 1 and adj > len(str((-context.Etiny()+1)*3)):
- # underflow to 0
- ans = _dec_from_triple(0, '1', context.Etiny()-1)
- elif self._sign == 0 and adj < -p:
- # p+1 digits; final round will raise correct flags
- ans = _dec_from_triple(0, '1' + '0'*(p-1) + '1', -p)
- elif self._sign == 1 and adj < -p-1:
- # p+1 digits; final round will raise correct flags
- ans = _dec_from_triple(0, '9'*(p+1), -p-1)
- # general case
- else:
- op = _WorkRep(self)
- c, e = op.int, op.exp
- if op.sign == 1:
- c = -c
-
- # compute correctly rounded result: increase precision by
- # 3 digits at a time until we get an unambiguously
- # roundable result
- extra = 3
- while True:
- coeff, exp = _dexp(c, e, p+extra)
- if coeff % (5*10**(len(str(coeff))-p-1)):
- break
- extra += 3
+ (twix
+ (let (get-context context code))
+
+ ;; exp(NaN) = NaN
+ (let ((ans ((ref self '_check_nans) #:context context))))
+
+ (ans it it)
- ans = _dec_from_triple(0, str(coeff), exp)
+ ;; exp(-Infinity) = 0
+ ((= ((ref self '_isinfinity)) -1)
+ _Zero)
- # at this stage, ans should round correctly with *any*
- # rounding mode, not just with ROUND_HALF_EVEN
- context = context._shallow_copy()
- rounding = context._set_rounding(ROUND_HALF_EVEN)
- ans = ans._fix(context)
- context.rounding = rounding
+ ;; exp(0) = 1
+ ((not (bool self))
+ _One)
- return ans
+ ;; exp(Infinity) = Infinity
+ ((= ((ref self '_isinfinity)) 1)
+ (Decimal self))
- def is_canonical(self):
- """Return True if self is canonical; otherwise return False.
+ ;; the result is now guaranteed to be inexact (the true
+ ;; mathematical result is transcendental). There's no need to
+ ;; raise Rounded and Inexact here---they'll always be raised as
+ ;; a result of the call to _fix.
+ (let ((p (ctx-prec context))
+ (adj ((ref self 'adjusted)))))
+
+ ;; we only need to do any computation for quite a small range
+ ;; of adjusted exponents---for example, -29 <= adj <= 10 for
+ ;; the default context. For smaller exponent the result is
+ ;; indistinguishable from 1 at the given precision, while for
+ ;; larger exponent the result either overflows or underflows.
+ (let* ((sign (ref self '_sign))
+ (emax (ctx-emax context))
+ (etiny (ctx-etiny context))
+ (ans
+ (cond
+ ((and (= sign 0)
+ (> adj (len (str (* (+ emax 1) 3)))))
+ ;; overflow
+ (_dec_from_triple 0 "1" (+ emax 1)))
+
+ ((and (= sign 1)
+ (> adj (len (str (* (- 1 etiny) 3)))))
+ ;; underflow to 0
+ (_dec_from_triple 0 "1" (- etiny 1)))
+ ((and (= sign 0) (< adj (- p)))
+ ;; p+1 digits; final round will raise correct flags
+ (_dec_from_triple 0 (+ "1" (* "0" (- p 1)) "1") (- p)))
+ ((and (= sign 1) (< adj (- (+ p 1))))
+ ;; p+1 digits; final round will raise correct flags
+ (_dec_from_triple 0 (* "9" (+ p 1)) (- (+ p 1))))
+ (else
+ ;; general case
+ (let* ((op (_WorkRep self))
+ (c (ref op 'int))
+ (e (ref op 'exp)))
+ (if (= (ref op 'sign) 1)
+ (set! c (- c)))
+
+ ;; compute correctly rounded result: increase precision by
+ ;; 3 digits at a time until we get an unambiguously
+ ;; roundable result
+
+ (let lp ((extra 3))
+ (call-with-values (lambda () (_dexp c e (+ p extra)))
+ (lambda (coeff exp)
+ (if (not (= (modulo
+ coeff
+ (* 5 (expt 10
+ (- (len (str coeff)) p 1))))
+ 0))
+
+ (_dec_from_triple 0 (str coeff) exp)
+ (lp (+ ex 3))))))))))))
+
+ ;; at this stage, ans should round correctly with *any*
+ ;; rounding mode, not just with ROUND_HALF_EVEN
+
+ (let* ((context ((ref context '_shallow_copy)))
+ (rounding ((ref context '_set_rounding) ROUND_HALF_EVEN))
+ (ans ((ref ans '_fix) context)))
+
+ (set context 'rounding rounding)
+ ans))))
+
+ (define is_canonical
+ (lambda (self)
+ "Return True if self is canonical; otherwise return False.
Currently, the encoding of a Decimal instance is always
canonical, so this method returns True for any Decimal.
- """
- return True
+ "
+ #t))
- def is_finite(self):
- """Return True if self is finite; otherwise return False.
+ (define is_finite
+ (lambda (self)
+ "Return True if self is finite; otherwise return False.
A Decimal instance is considered finite if it is neither
infinite nor a NaN.
- """
- return not self._is_special
-
- def is_infinite(self):
- """Return True if self is infinite; otherwise return False."""
- return self._exp == 'F'
-
- def is_nan(self):
- """Return True if self is a qNaN or sNaN; otherwise return False."""
- return self._exp in ('n', 'N')
-
- def is_normal(self, context=None):
- """Return True if self is a normal number; otherwise return False."""
- if self._is_special or not self:
- return False
- if context is None:
- context = getcontext()
- return context.Emin <= self.adjusted()
-
- def is_qnan(self):
- """Return True if self is a quiet NaN; otherwise return False."""
- return self._exp == 'n'
-
- def is_signed(self):
- """Return True if self is negative; otherwise return False."""
- return self._sign == 1
-
- def is_snan(self):
- """Return True if self is a signaling NaN; otherwise return False."""
- return self._exp == 'N'
-
- def is_subnormal(self, context=None):
- """Return True if self is subnormal; otherwise return False."""
- if self._is_special or not self:
- return False
- if context is None:
- context = getcontext()
- return self.adjusted() < context.Emin
-
- def is_zero(self):
- """Return True if self is a zero; otherwise return False."""
- return not self._is_special and self._int == '0'
-
- def _ln_exp_bound(self):
- """Compute a lower bound for the adjusted exponent of self.ln().
+ "
+ (not (ref self '_is_special))))
+
+ (define is_infinite
+ (lambda (self)
+ "Return True if self is infinite; otherwise return False."
+ (equal? (ref self '_exp) "F")))
+
+ (define is_nan
+ (lambda (self)
+ "Return True if self is a qNaN or sNaN; otherwise return False."
+ (let ((e (ref self '_exp)))
+ (or (equal? e "n") (equal? e "N")))))
+
+ (define is_normal
+ (lam (self (= context None))
+ "Return True if self is a normal number; otherwise return False."
+ (if (or (bool (ref self '_is_special)) (not (bool self)))
+ #f
+ (let ((context (if (eq? context None)
+ (getcontext)
+ context)))
+ (<= (cttx-emin context) ((ref self 'adjusted)))))))
+
+ (define is_qnan
+ (lambda (self)
+ "Return True if self is a quiet NaN; otherwise return False."
+ (equal? (ref self '_exp) "n")))
+
+ (define is_signed
+ (lambda (self)
+ "Return True if self is negative; otherwise return False."
+ (= (ref self '_sign) 1 )))
+
+ (define is_snan
+ (lambda (self)
+ "Return True if self is a signaling NaN; otherwise return False."
+ (equal? (ref self '_exp) "N")))
+
+ (define is_subnormal
+ (lam (self (= context None))
+ "Return True if self is subnormal; otherwise return False."
+ (if (or (bool (ref self '_is_special)) (not (bool self)))
+ #f
+ (let ((context (if (eq? context None)
+ (getcontext)
+ context)))
+ (> (cttx-emin context) ((ref self 'adjusted)))))))
+
+ (define is_zero
+ (lambda (self)
+ "Return True if self is a zero; otherwise return False."
+ (and (not (bool (ref self '_is_special)))
+ (equal? (ref self '_int) "0"))))
+
+ (define _ln_exp_bound
+ (lambda (self)
+ "Compute a lower bound for the adjusted exponent of self.ln().
In other words, compute r such that self.ln() >= 10**r. Assumes
that self is finite and positive and that self != 1.
- """
+ "
+
+ ;; for 0.1 <= x <= 10 we use the inequalities 1-1/x <= ln(x) <= x-1
+ (let ((adj (+ (ref self '_exp) (len (ref self '_int)) (- 1))))
+ (cond
+ ((>= adj 1)
+ ;; argument >= 10; we use 23/10 = 2.3 as a lower bound for ln(10)
+ (- (len (str (floordiv (* adj 23) 10))) 1))
+ ((<= adj -2)
+ ;; argument <= 0.1
+ (- (len (str (floordiv (* (- (+ 1 adj)) 23) 10))) 1))
+ (else
+ (let* ((op (_WorkRep self))
+ (c (ref op 'int))
+ (e (ref op 'exp)))
+ (if (= adj 0)
+ ;; 1 < self < 10
+ (let ((num (str (- c (expt 10 (- e)))))
+ (den (str c)))
+ (- (len num) (len den) - (< num den)))
+ ;; adj == -1, 0.1 <= self < 1
+ (+ e (len (str (- (expt 10 (- e)) c))) (- 1)))))))))
+
+
+ (define ln
+ (lam (self (= context None))
+ "Returns the natural (base e) logarithm of self."
- # for 0.1 <= x <= 10 we use the inequalities 1-1/x <= ln(x) <= x-1
- adj = self._exp + len(self._int) - 1
- if adj >= 1:
- # argument >= 10; we use 23/10 = 2.3 as a lower bound for ln(10)
- return len(str(adj*23//10)) - 1
- if adj <= -2:
- # argument <= 0.1
- return len(str((-1-adj)*23//10)) - 1
- op = _WorkRep(self)
- c, e = op.int, op.exp
- if adj == 0:
- # 1 < self < 10
- num = str(c-10**-e)
- den = str(c)
- return len(num) - len(den) - (num < den)
- # adj == -1, 0.1 <= self < 1
- return e + len(str(10**-e - c)) - 1
-
-
- def ln(self, context=None):
- """Returns the natural (base e) logarithm of self."""
-
- if context is None:
- context = getcontext()
-
- # ln(NaN) = NaN
- ans = self._check_nans(context=context)
- if ans:
- return ans
-
- # ln(0.0) == -Infinity
- if not self:
- return _NegativeInfinity
-
- # ln(Infinity) = Infinity
- if self._isinfinity() == 1:
- return _Infinity
-
- # ln(1.0) == 0.0
- if self == _One:
- return _Zero
-
- # ln(negative) raises InvalidOperation
- if self._sign == 1:
- return context._raise_error(InvalidOperation,
- 'ln of a negative value')
-
- # result is irrational, so necessarily inexact
- op = _WorkRep(self)
- c, e = op.int, op.exp
- p = context.prec
-
- # correctly rounded result: repeatedly increase precision by 3
- # until we get an unambiguously roundable result
- places = p - self._ln_exp_bound() + 2 # at least p+3 places
- while True:
- coeff = _dlog(c, e, places)
- # assert len(str(abs(coeff)))-p >= 1
- if coeff % (5*10**(len(str(abs(coeff)))-p-1)):
- break
- places += 3
- ans = _dec_from_triple(int(coeff<0), str(abs(coeff)), -places)
-
- context = context._shallow_copy()
- rounding = context._set_rounding(ROUND_HALF_EVEN)
- ans = ans._fix(context)
- context.rounding = rounding
- return ans
-
- def _log10_exp_bound(self):
- """Compute a lower bound for the adjusted exponent of self.log10().
+ (twix
+ (let (get-context context code))
+
+ ;; ln(NaN) = NaN
+ (let ((ans ((ref self '_check_nans) #:context context))))
+ (ans it it)
+
+ ;; ln(0.0) == -Infinity
+ ((not (bool self))
+ _NegativeInfinity)
+
+ ;; ln(Infinity) = Infinity
+ ((= ((ref self '_isinfinity)) 1)
+ _Infinity)
+
+ ;; ln(1.0) == 0.0
+ (if (equal? self _One)
+ _Zero)
+
+ ;; ln(negative) raises InvalidOperation
+ ((= (ref self '_sign) 1)
+ ((ctx-error context) InvalidOperation,
+ "ln of a negative value"))
+
+ ;; result is irrational, so necessarily inexact
+ (let* ((op (_WorkRep self))
+ (c (ref op 'int))
+ (e (ref op 'exp))
+ (p (ctx-prec context))))
+
+
+ ;; correctly rounded result: repeatedly increase precision by 3
+ ;; until we get an unambiguously roundable result
+ (let ((places (+ p
+ (- ((ref self '_ln_exp_bound)))
+ 2)) ;; at least p+3 places
+ (ans #f))
+ (let lp ((places places))
+ (let ((coeff (_dlog c e places)))
+ ;; assert len(str(abs(coeff)))-p >= 1
+ (if (not (= (modulo coeff
+ (* 5 (expr 10 (- (len (str (abs coeff)))
+ p 1))))
+ 0))
+ (set! ans (_dec_from_triple (int (< coeff 0))
+ (str (abs coeff))
+ (- places)))
+
+ (lp (+ places 3)))))
+
+ (let* ((context ((ref context '_shallow_copy)))
+ (rounding ((ref context '_set_rounding) ROUND_HALF_EVEN))
+ (ans ((ref ans '_fix) context)))
+ (set context 'rounding rounding)
+ ans)))))
+
+ (define _log10_exp_bound
+ (lambda (self)
+ "Compute a lower bound for the adjusted exponent of self.log10().
In other words, find r such that self.log10() >= 10**r.
Assumes that self is finite and positive and that self != 1.
- """
+ "
- # For x >= 10 or x < 0.1 we only need a bound on the integer
- # part of log10(self), and this comes directly from the
- # exponent of x. For 0.1 <= x <= 10 we use the inequalities
- # 1-1/x <= log(x) <= x-1. If x > 1 we have |log10(x)| >
- # (1-1/x)/2.31 > 0. If x < 1 then |log10(x)| > (1-x)/2.31 > 0
-
- adj = self._exp + len(self._int) - 1
- if adj >= 1:
- # self >= 10
- return len(str(adj))-1
- if adj <= -2:
- # self < 0.1
- return len(str(-1-adj))-1
- op = _WorkRep(self)
- c, e = op.int, op.exp
- if adj == 0:
- # 1 < self < 10
- num = str(c-10**-e)
- den = str(231*c)
- return len(num) - len(den) - (num < den) + 2
- # adj == -1, 0.1 <= self < 1
- num = str(10**-e-c)
- return len(num) + e - (num < "231") - 1
-
- def log10(self, context=None):
- """Returns the base 10 logarithm of self."""
-
- if context is None:
- context = getcontext()
-
- # log10(NaN) = NaN
- ans = self._check_nans(context=context)
- if ans:
- return ans
-
- # log10(0.0) == -Infinity
- if not self:
- return _NegativeInfinity
-
- # log10(Infinity) = Infinity
- if self._isinfinity() == 1:
- return _Infinity
-
- # log10(negative or -Infinity) raises InvalidOperation
- if self._sign == 1:
- return context._raise_error(InvalidOperation,
- 'log10 of a negative value')
-
- # log10(10**n) = n
- if self._int[0] == '1' and self._int[1:] == '0'*(len(self._int) - 1):
- # answer may need rounding
- ans = Decimal(self._exp + len(self._int) - 1)
- else:
- # result is irrational, so necessarily inexact
- op = _WorkRep(self)
- c, e = op.int, op.exp
- p = context.prec
-
- # correctly rounded result: repeatedly increase precision
- # until result is unambiguously roundable
- places = p-self._log10_exp_bound()+2
- while True:
- coeff = _dlog10(c, e, places)
- # assert len(str(abs(coeff)))-p >= 1
- if coeff % (5*10**(len(str(abs(coeff)))-p-1)):
- break
- places += 3
- ans = _dec_from_triple(int(coeff<0), str(abs(coeff)), -places)
+ ;; For x >= 10 or x < 0.1 we only need a bound on the integer
+ ;; part of log10(self), and this comes directly from the
+ ;; exponent of x. For 0.1 <= x <= 10 we use the inequalities
+ ;; 1-1/x <= log(x) <= x-1. If x > 1 we have |log10(x)| >
+ ;; (1-1/x)/2.31 > 0. If x < 1 then |log10(x)| > (1-x)/2.31 > 0
- context = context._shallow_copy()
- rounding = context._set_rounding(ROUND_HALF_EVEN)
- ans = ans._fix(context)
- context.rounding = rounding
- return ans
+ (let ((adj (+ (ref self '_exp) (len (ref self '_int)) (- 1))))
+ (cond
+ ((>=adj 1)
+ ;; self >= 10
+ (- (len (str adj)) 1))
+ ((<= adj -2)
+ ;;# self < 0.1
+ (- (len (str (- (+ 1 adj)))) 1))
+ (else
+ (let* ((op (_WorkRep self))
+ (c (ref op 'int))
+ (e (ref op 'exp)))
+ (if(= adj 0)
+ ;; 1 < self < 10
+ (let ((num (str (- c (expt 10 (- e)))))
+ (den (str (* 231 c))))
+ (+ (len num) (- (len den)) (- (< num den)) 2))
+ ;; adj == -1, 0.1 <= self < 1
+ (let ((num (str (- (expt 10 (- e)) c))))
+ (+ (len num) e (- (< num "231")) (- 1))))))))))
+
+ (define log10
+ (lam (self (= context None))
+ "Returns the base 10 logarithm of self."
+ (twix
+ (let (get-context context code))
+
+ ;; log(NaN) = NaN
+ (let ((ans ((ref self '_check_nans) #:context context))))
+ (ans it it)
+
+ ;; log10(0.0) == -Infinity
+ ((not (bool self))
+ _NegativeInfinity)
+
+ ;; log10(Infinity) = Infinity
+ ((= ((ref self '_isinfinity)) 1)
+ _Infinity)
+
+ ;; log10(1.0) == 0.0
+ (if (equal? self _One)
+ _Zero)
- def logb(self, context=None):
- """ Returns the exponent of the magnitude of self's MSD.
+ ;; ln(negative) raises InvalidOperation
+ ((= (ref self '_sign) 1)
+ ((ctx-error context) InvalidOperation,
+ "log10 of a negative value"))
+
+ (let ((ans #f)))
+
+ ;; log10(10**n) = n
+ (begin
+ (if (and (equal? (string-ref (ref self '_int) 0) #\1)
+ (equal? (pylist-slice (ref self '_int) 1 None None)
+ (* "0" (- (len (ref self '_int)) 1))))
+ ;;answer may need rounding
+ (set! ans (Decimal (+ self._exp (len (ref self '_int)) (- 1))))
+ ;; result is irrational, so necessarily inexact
+ (let* ((op (_WorkRep self))
+ (c (ref op 'int))
+ (e (ref op 'exp))
+ (p (ctx-prec context)))
+
+ ;; correctly rounded result: repeatedly increase precision
+ ;; until result is unambiguously roundable
+ (let lp ((places (+ p (- ((ref self '_log10_exp_bound))) 2)))
+ (let ((coeff (_dlog10 c e places)))
+ ;; assert len(str(abs(coeff)))-p >= 1
+ (if (not (= (modulo coeff
+ (* 5 (expt 10
+ (- (len (str (abs coeff)))
+ p 1))))
+ 0))
+ (set! ans
+ (_dec_from_triple (int (< coeff 0))
+ (str (abs coeff))
+ (- places)))
+ (lp (+ places 3)))))))
+
+ (let* ((context ((ref context '_shallow_copy)))
+ (rounding ((ref context '_set_rounding) ROUND_HALF_EVEN))
+ (ans ((ref ans '_fix) context)))
+ (set context 'rounding rounding)
+ ans)))))
+
+ (define logb
+ (lam (self (= context None))
+ " Returns the exponent of the magnitude of self's MSD.
The result is the integer which is the exponent of the magnitude
of the most significant digit of self (as though it were truncated
to a single digit while maintaining the value of that digit and
without limiting the resulting exponent).
- """
- # logb(NaN) = NaN
- ans = self._check_nans(context=context)
- if ans:
- return ans
+ "
+ (twix
+ (let (get-context context code))
- if context is None:
- context = getcontext()
+ ;; logb(NaN) = NaN
+ (let ((ans ((ref self '_check_nans) #:context context))))
+ (ans it it)
- # logb(+/-Inf) = +Inf
- if self._isinfinity():
- return _Infinity
+ ;; logb(+/-Inf) = +Inf
+ (((ref self '_isinfinity))
+ _Infinity)
- # logb(0) = -Inf, DivisionByZero
- if not self:
- return context._raise_error(DivisionByZero, 'logb(0)', 1)
+ ;; logb(0) = -Inf, DivisionByZero
+ ((not (bool self))
+ ((ctx-error context) DivisionByZero "logb(0)" 1))
- # otherwise, simply return the adjusted exponent of self, as a
- # Decimal. Note that no attempt is made to fit the result
- # into the current context.
- ans = Decimal(self.adjusted())
- return ans._fix(context)
+ ;; otherwise, simply return the adjusted exponent of self, as a
+ ;; Decimal. Note that no attempt is made to fit the result
+ ;; into the current context.
+ (let ((ans (Decimal ((ref self 'adjusted)))))
+ ((ref ans '_fix) context)))))
- def _islogical(self):
- """Return True if self is a logical operand.
+ (define _islogical
+ (lambda (self)
+ "Return True if self is a logical operand.
For being logical, it must be a finite number with a sign of 0,
an exponent of 0, and a coefficient whose digits must all be
either 0 or 1.
- """
- if self._sign != 0 or self._exp != 0:
- return False
- for dig in self._int:
- if dig not in '01':
- return False
- return True
-
- def _fill_logical(self, context, opa, opb):
- dif = context.prec - len(opa)
- if dif > 0:
- opa = '0'*dif + opa
- elif dif < 0:
- opa = opa[-context.prec:]
- dif = context.prec - len(opb)
- if dif > 0:
- opb = '0'*dif + opb
- elif dif < 0:
- opb = opb[-context.prec:]
- return opa, opb
-
- def logical_and(self, other, context=None):
- """Applies an 'and' operation between self and other's digits."""
- if context is None:
- context = getcontext()
-
- other = _convert_other(other, raiseit=True)
-
- if not self._islogical() or not other._islogical():
- return context._raise_error(InvalidOperation)
-
- # fill to context.prec
- (opa, opb) = self._fill_logical(context, self._int, other._int)
-
- # make the operation, and clean starting zeroes
- result = "".join([str(int(a)&int(b)) for a,b in zip(opa,opb)])
- return _dec_from_triple(0, result.lstrip('0') or '0', 0)
-
- def logical_invert(self, context=None):
- """Invert all its digits."""
- if context is None:
- context = getcontext()
- return self.logical_xor(_dec_from_triple(0,'1'*context.prec,0),
- context)
-
- def logical_or(self, other, context=None):
- """Applies an 'or' operation between self and other's digits."""
- if context is None:
- context = getcontext()
-
- other = _convert_other(other, raiseit=True)
-
- if not self._islogical() or not other._islogical():
- return context._raise_error(InvalidOperation)
-
- # fill to context.prec
- (opa, opb) = self._fill_logical(context, self._int, other._int)
-
- # make the operation, and clean starting zeroes
- result = "".join([str(int(a)|int(b)) for a,b in zip(opa,opb)])
- return _dec_from_triple(0, result.lstrip('0') or '0', 0)
-
- def logical_xor(self, other, context=None):
- """Applies an 'xor' operation between self and other's digits."""
- if context is None:
- context = getcontext()
-
- other = _convert_other(other, raiseit=True)
-
- if not self._islogical() or not other._islogical():
- return context._raise_error(InvalidOperation)
-
- # fill to context.prec
- (opa, opb) = self._fill_logical(context, self._int, other._int)
-
- # make the operation, and clean starting zeroes
- result = "".join([str(int(a)^int(b)) for a,b in zip(opa,opb)])
- return _dec_from_triple(0, result.lstrip('0') or '0', 0)
-
- def max_mag(self, other, context=None):
- """Compares the values numerically with their sign ignored."""
- other = _convert_other(other, raiseit=True)
-
- if context is None:
- context = getcontext()
-
- if self._is_special or other._is_special:
- # If one operand is a quiet NaN and the other is number, then the
- # number is always returned
- sn = self._isnan()
- on = other._isnan()
- if sn or on:
- if on == 1 and sn == 0:
- return self._fix(context)
- if sn == 1 and on == 0:
- return other._fix(context)
- return self._check_nans(other, context)
-
- c = self.copy_abs()._cmp(other.copy_abs())
- if c == 0:
- c = self.compare_total(other)
-
- if c == -1:
- ans = other
- else:
- ans = self
-
- return ans._fix(context)
-
- def min_mag(self, other, context=None):
- """Compares the values numerically with their sign ignored."""
- other = _convert_other(other, raiseit=True)
-
- if context is None:
- context = getcontext()
-
- if self._is_special or other._is_special:
- # If one operand is a quiet NaN and the other is number, then the
- # number is always returned
- sn = self._isnan()
- on = other._isnan()
- if sn or on:
- if on == 1 and sn == 0:
- return self._fix(context)
- if sn == 1 and on == 0:
- return other._fix(context)
- return self._check_nans(other, context)
-
- c = self.copy_abs()._cmp(other.copy_abs())
- if c == 0:
- c = self.compare_total(other)
-
- if c == -1:
- ans = self
- else:
- ans = other
+ "
+ (if (or (not (= (ref self '_sign) 0))
+ (not (= (ref self '_exp) 0)))
+ #f
+ (for ((dig : (ref self '_int))) ()
+ (if (not (or (equal? dig "0") (equal? dig "1")))
+ (break #f))
+ #:final #t))))
+
+ (define _fill_logical
+ (lambda (self context opa opb)
+ (define (o opa dif)
+ (cond
+ ((> dif 0)
+ (* "0" dif) opa)
+ ((< dif 0)
+ (pylist-slice opa (- (ctx-prec context) None None)))
+ (else
+ opa)))
+
+ (let* ((dif (- (ctx-prec context) (len opa)))
+ (opa (o opa diff))
+ (dif (- (ctx-prec context) (len opb)))
+ (opb (o opb diff)))
+ (values opa opb))))
+
+ (define logical_*
+ (lambda (logand)
+ (lam (self other (= context None))
+ "Applies an 'and' operation between self and other's digits."
+
+ (twix
+ (let (get-context context code))
+ (let ((other (_convert_other other #:raiseit #t))))
+
+ ((or (not ((ref self '_islogical)) (not ((ref other '_islogical)))))
+ ((ctx-error context) InvalidOperation))
+
+ ;; fill to context.prec
+ (call-with-values
+ (lambda ()
+ ((ref self '_fill_logical)
+ context (ref self '_int) (ref other '_int)))
+ (lambda (opa opb)
+ ;; make the operation, and clean starting zeroes
+ (_dec_from_triple
+ 0
+ (for ((a : opa) (b : opb)) ((l '()) (f #t))
+ (let ((i (logand (int a) (int b))))
+ (if (and f (= i 0))
+ (values l #t)
+ (values (cons (str i) l) #f)))
+ #:final
+ (if (null? l)
+ "0"
+ (list->string (reverse l))))
+ 0)))))))
+
+ (define logical_and (logical_* logand))
+ (define logical_or (logical_* logior))
+ (define logical_xor (logical_* logxor))
+
+ (define logical_invert
+ (lam (self (= context None))
+ "Invert all its digits."
+ (let ((context (if (eq? context None)
+ (getcontext)
+ context)))
+ (logical_xor self
+ (_dec_from_triple 0 (* "1" (ctx-prec context)) 0)
+ context))))
- return ans._fix(context)
- def next_minus(self, context=None):
- """Returns the largest representable number smaller than itself."""
- if context is None:
- context = getcontext()
+ (define max_mag
+ (lambda (self other (= context None))
+ "Compares the values numerically with their sign ignored."
+ (twix
+ (let ((other (_convert_other other #:raiseit #t))))
+ (let (get-context context code))
+
+ ((if (or (bool (ref self '_is_special)) (bool (other '_is_special)))
+ ;; If one operand is a quiet NaN and the other is number, then the
+ ;; number is always returned
+ (let ((sn ((ref self '_isnan)))
+ (on ((ref other '_isnan))))
+ (if (or (bool sn) (bool on))
+ (cond
+ ((and (= on 1) (= sn 0))
+ ((ref self '_fix) context))
+ ((and (= on 0) (= sn 1))
+ ((ref other '_fix) context))
+ (else
+ ((ref self '_check_nans) other context)))
+ #f))
+ #f) it it)
- ans = self._check_nans(context=context)
- if ans:
- return ans
+ (let* ((s ((ref self 'copy_abs)))
+ (o ((ref other 'copy_abs)))
+ (c ((ref s '_cmp) o))
+ (c (if (= c 0)
+ ((self 'compare_total) other)
+ c))
+ (ans (if (= c -1) other self)))
+
+ ((ref ans '_fix) context)))))
- if self._isinfinity() == -1:
- return _NegativeInfinity
- if self._isinfinity() == 1:
- return _dec_from_triple(0, '9'*context.prec, context.Etop())
+ (define x_mag
+ (lamnda (nott)
+ (lambda (self other (= context None))
+ "Compares the values numerically with their sign ignored."
+ (twix
+ (let ((other (_convert_other other #:raiseit #t))))
+ (let (get-context context code))
+
+ ((if (or (bool (ref self '_is_special)) (bool (other '_is_special)))
+ ;; If one operand is a quiet NaN and the other is number, then the
+ ;; number is always returned
+ (let ((sn ((ref self '_isnan)))
+ (on ((ref other '_isnan))))
+ (if (or (bool sn) (bool on))
+ (cond
+ ((and (= on 1) (= sn 0))
+ ((ref self '_fix) context))
+ ((and (= on 0) (= sn 1))
+ ((ref other '_fix) context))
+ (else
+ ((ref self '_check_nans) other context)))
+ #f))
+ #f) it it)
- context = context.copy()
- context._set_rounding(ROUND_FLOOR)
- context._ignore_all_flags()
- new_self = self._fix(context)
- if new_self != self:
- return new_self
- return self.__sub__(_dec_from_triple(0, '1', context.Etiny()-1),
- context)
-
- def next_plus(self, context=None):
- """Returns the smallest representable number larger than itself."""
- if context is None:
- context = getcontext()
-
- ans = self._check_nans(context=context)
- if ans:
- return ans
-
- if self._isinfinity() == 1:
- return _Infinity
- if self._isinfinity() == -1:
- return _dec_from_triple(1, '9'*context.prec, context.Etop())
+ (let* ((s ((ref self 'copy_abs)))
+ (o ((ref other 'copy_abs)))
+ (c ((ref s '_cmp) o))
+ (c (if (= c 0)
+ ((self 'compare_total) other)
+ c))
+ (ans (if (nott (= c -1)) other self)))
+
+ ((ref ans '_fix) context))))))
- context = context.copy()
- context._set_rounding(ROUND_CEILING)
- context._ignore_all_flags()
- new_self = self._fix(context)
- if new_self != self:
- return new_self
- return self.__add__(_dec_from_triple(0, '1', context.Etiny()-1),
- context)
+ (define max_mag (x_mag (lambda (x) x)))
+ (define min_mag (x_mag not))
+
+ (define next_minus
+ (lam (self (= context None))
+ "Returns the largest representable number smaller than itself."
- def next_toward(self, other, context=None):
- """Returns the number closest to self, in the direction towards other.
+ (twix
+ (let (get-context context code))
+
+ (let ((ans ((ref self '_check_nans) #:context context))))
+ (ans it it)
+
+ ((= ((ref self '_isinfinity)) -1)
+ _NegativeInfinity)
+
+ ((= ((ref self '_isinfinity)) 1)
+ (_dec_from_triple 0 (* '9' (ctx-prec context)) (ctx-etop context)))
+
+
+ (let* ((context ((ref context 'copy)))
+ (rounding ((ref context '_set_rounding) ROUND_FLOOR)))
+ ((context '_ignore_all_flags))
+ (let ((new_self ((ref self '_fix) context)))
+ (if (not (equal? self new_self))
+ new_self
+ ((ref self '__sub__)
+ (_dec_from_triple 0 "1" (- (ctx-etiny context) 1))
+ context)))))))
+
+ (define next_plus
+ (lam (self (= context None))
+ "Returns the largest representable number smaller than itself."
+
+ (twix
+ (let (get-context context code))
+
+ (let ((ans ((ref self '_check_nans) #:context context))))
+ (ans it it)
+
+ ((= ((ref self '_isinfinity)) 1)
+ _Infinity)
+
+ ((= ((ref self '_isinfinity)) -1)
+ (_dec_from_triple 1 (* '9' (ctx-prec context)) (ctx-etop context)))
+
+
+ (let* ((context ((ref context 'copy)))
+ (rounding ((ref context '_set_rounding) ROUND_CEILING)))
+ ((context '_ignore_all_flags))
+ (let ((new_self ((ref self '_fix) context)))
+ (if (not (equal? self new_self))
+ new_self
+ ((ref self '__add__)
+ (_dec_from_triple 0 "1" (- (ctx-etiny context) 1))
+ context)))))))
+
+ (define next_toward
+ (lam (self other (= context None))
+ "Returns the number closest to self, in the direction towards other.
The result is the closest representable number to self
(excluding self) that is in the direction towards other,
unless both have the same value. If the two operands are
numerically equal, then the result is a copy of self with the
sign set to be the same as the sign of other.
- """
- other = _convert_other(other, raiseit=True)
-
- if context is None:
- context = getcontext()
-
- ans = self._check_nans(other, context)
- if ans:
- return ans
-
- comparison = self._cmp(other)
- if comparison == 0:
- return self.copy_sign(other)
-
- if comparison == -1:
- ans = self.next_plus(context)
- else: # comparison == 1
- ans = self.next_minus(context)
-
- # decide which flags to raise using value of ans
- if ans._isinfinity():
- context._raise_error(Overflow,
- 'Infinite result from next_toward',
- ans._sign)
- context._raise_error(Inexact)
- context._raise_error(Rounded)
- elif ans.adjusted() < context.Emin:
- context._raise_error(Underflow)
- context._raise_error(Subnormal)
- context._raise_error(Inexact)
- context._raise_error(Rounded)
- # if precision == 1 then we don't raise Clamped for a
- # result 0E-Etiny.
- if not ans:
- context._raise_error(Clamped)
-
- return ans
-
- def number_class(self, context=None):
- """Returns an indication of the class of self.
+ "
+
+ (twix
+ (let ((other (_convert_other other #:raiseit #t))))
+ (let (get-context context code))
+
+ (let ((ans ((ref self '_check_nans) #:context context))))
+ (ans it it)
+
+ (let ((comparison ((ref self '_cmp) other))))
+
+ ((= comparison 0)
+ ((ref self 'copy_sign) other))
+
+ (let ((ans (if (= comparison -1)
+ ((ref self 'next_plus) context)
+ ;; comparison == 1
+ ((ref self 'next_minus) context))))
+
+ ;; decide which flags to raise using value of ans
+ (cond
+ (((ref ans '_isinfinity))
+ ((ctx-error context) Overflow "Infinite result from next_toward"
+ (ref ans '_sign))
+ ((ctx-error context) Inexact)
+ ((ctx-error context) Rounded))
+
+ ((< ((ref ans 'adjusted)) (ctx-emin context))
+ ((ctx-error context) Underflow)
+ ((ctx-error context) Subnormal)
+ ((ctx-error context) Inexact)
+ ((ctx-error context) Rounded)
+ ;; if precision == 1 then we don't raise Clamped for a
+ ;; result 0E-Etiny.
+ (if (not (bool ans))
+ ((ctx-error context) Clamped)))
+ (else #f))
+
+ ans))))
+
+ (define number_class
+ (lam (self (= context None))
+ "Returns an indication of the class of self.
The class is one of the following strings:
sNaN
@@ -3782,231 +3896,271 @@
+Subnormal
+Normal
+Infinity
- """
- if self.is_snan():
- return "sNaN"
- if self.is_qnan():
- return "NaN"
- inf = self._isinfinity()
- if inf == 1:
- return "+Infinity"
- if inf == -1:
- return "-Infinity"
- if self.is_zero():
- if self._sign:
- return "-Zero"
- else:
- return "+Zero"
- if context is None:
- context = getcontext()
- if self.is_subnormal(context=context):
- if self._sign:
- return "-Subnormal"
- else:
- return "+Subnormal"
- # just a normal, regular, boring number, :)
- if self._sign:
- return "-Normal"
- else:
- return "+Normal"
+ "
+ (twix
+ (((ref self 'is_snan)) it
+ "sNaN")
+ (((ref self 'is_qnan)) it
+ "NaN")
+ (let ((inf ((ref self '_isinfinity)))))
+ ((= inf 1) it
+ "+Infinity")
+ ((= inf -1) it
+ "-Infinity")
+ (((ref self 'is_zero)) it
+ (if (bool (ref self '_sign))
+ "-Zero"
+ "+Zero"))
+
+ (let (get-context context code))
+
+ (((ref self 'is_subnormal) #:context context)
+ (if (bool (ref self '_sign))
+ "-Subnormal"
+ "+Subnormal"))
+
+ ;; just a normal, regular, boring number, :)
+ (if (bool (ref self '_sign))
+ "-Normal"
+ "+Normal"))))
+
+ (define radix
+ (lambda (self)
+ "Just returns 10, as this is Decimal"
+ (Decimal 10)))
- def radix(self):
- """Just returns 10, as this is Decimal, :)"""
- return Decimal(10)
+ (define rotate
+ (lam (self other (= context None))
+ "Returns a rotated copy of self, value-of-other times."
+ (twix
+ (let (get-context context code))
+ (let ((other (_convert_other other #:raiseit #t))))
- def rotate(self, other, context=None):
- """Returns a rotated copy of self, value-of-other times."""
- if context is None:
- context = getcontext()
-
- other = _convert_other(other, raiseit=True)
-
- ans = self._check_nans(other, context)
- if ans:
- return ans
-
- if other._exp != 0:
- return context._raise_error(InvalidOperation)
- if not (-context.prec <= int(other) <= context.prec):
- return context._raise_error(InvalidOperation)
-
- if self._isinfinity():
- return Decimal(self)
-
- # get values, pad if necessary
- torot = int(other)
- rotdig = self._int
- topad = context.prec - len(rotdig)
- if topad > 0:
- rotdig = '0'*topad + rotdig
- elif topad < 0:
- rotdig = rotdig[-topad:]
-
- # let's rotate!
- rotated = rotdig[torot:] + rotdig[:torot]
- return _dec_from_triple(self._sign,
- rotated.lstrip('0') or '0', self._exp)
-
- def scaleb(self, other, context=None):
- """Returns self operand after adding the second value to its exp."""
- if context is None:
- context = getcontext()
-
- other = _convert_other(other, raiseit=True)
-
- ans = self._check_nans(other, context)
- if ans:
- return ans
-
- if other._exp != 0:
- return context._raise_error(InvalidOperation)
- liminf = -2 * (context.Emax + context.prec)
- limsup = 2 * (context.Emax + context.prec)
- if not (liminf <= int(other) <= limsup):
- return context._raise_error(InvalidOperation)
-
- if self._isinfinity():
- return Decimal(self)
-
- d = _dec_from_triple(self._sign, self._int, self._exp + int(other))
- d = d._fix(context)
- return d
-
- def shift(self, other, context=None):
- """Returns a shifted copy of self, value-of-other times."""
- if context is None:
- context = getcontext()
-
- other = _convert_other(other, raiseit=True)
-
- ans = self._check_nans(other, context)
- if ans:
- return ans
-
- if other._exp != 0:
- return context._raise_error(InvalidOperation)
- if not (-context.prec <= int(other) <= context.prec):
- return context._raise_error(InvalidOperation)
-
- if self._isinfinity():
- return Decimal(self)
-
- # get values, pad if necessary
- torot = int(other)
- rotdig = self._int
- topad = context.prec - len(rotdig)
- if topad > 0:
- rotdig = '0'*topad + rotdig
- elif topad < 0:
- rotdig = rotdig[-topad:]
-
- # let's shift!
- if torot < 0:
- shifted = rotdig[:torot]
- else:
- shifted = rotdig + '0'*torot
- shifted = shifted[-context.prec:]
+ (let ((ans ((ref other '_check_nans) #:context context))))
+ (ans it it)
+
+ ((not (= (ref other '_exp) 0))
+ ((ctx-error context) InvalidOperation))
- return _dec_from_triple(self._sign,
- shifted.lstrip('0') or '0', self._exp)
+ (let ((o (int other))
+ (p (ctx-prec context))))
+
+ ((not (and (<= (- p) o) (<= o p)))
+ ((ctx-error context) InvalidOperation))
- # Support for pickling, copy, and deepcopy
- def __reduce__(self):
- return (self.__class__, (str(self),))
+ (((ref self '_isinfinity))
+ (Decimal self))
- def __copy__(self):
- if type(self) is Decimal:
- return self # I'm immutable; therefore I am my own clone
- return self.__class__(str(self))
+ ;; get values, pad if necessary
+ (let ((torot (int other))
+ (rotdig (ref self '_int))
+ (topad (- p (len rotdig))))
+ (cond
+ ((> topad 0)
+ (set! rotdig (+ (* "0" topad) + rotdig)))
+ ((< topad 0)
+ (set! rotdig (pylist-slice rotdig (- topad) None None)))
+ (else #f))
+
+ (let ((rotated (+ (pylist-slice rotdig torot None None)
+ (pylist-slice rotdig None torot None))))
+ (_dec_from_triple (ref self '_sign)
+ (or (bool (py-lstrip rotated "0")) "0")
+ (ref self '_exp)))))))
+
+ (define scaleb
+ (lam (self other (= context None))
+ "Returns self operand after adding the second value to its exp."
+ (twix
+ (let (get-context context code))
+ (let ((other (_convert_other other #:raiseit #t))))
+
+ (let ((ans ((ref other '_check_nans) #:context context))))
+ (ans it it)
+
+ ((not (= (ref other '_exp)))
+ ((ctx-error context) InvalidOperation))
+
+ (let ((liminf (* -2 (+ (ctx-emax context) (ctx-prec context))))
+ (limsup (* 2 (+ (ctx-emax context) (ctx-prec context))))))
+
+ ((not (let ((o (int other)))
+ (and (<= liminf o)
+ (<= o limsup))))
+ ((ctx-error context) InvalidOperation))
+
+ (((ref self '_isinfinity))
+ (Decimal self))
+
+ (let* ((d (_dec_from_triple (ref self '_sign)
+ (ref self '_int)
+ (+ (ref self '_exp) (int other))))
+ (d ((ref d '_fix) context)))
+ d))))
+
+ (define shift
+ (lam (self other (= context None))
+ "Returns a rotated copy of self, value-of-other times."
+ (twix
+ (let (get-context context code))
+ (let ((other (_convert_other other #:raiseit #t))))
+
+ (let ((ans ((ref other '_check_nans) #:context context))))
+ (ans it it)
+
+ ((not (= (ref other '_exp) 0))
+ ((ctx-error context) InvalidOperation))
+
+ (let ((o (int other))
+ (p (ctx-prec context))))
+
+ ((not (and (<= (- p) o) (<= o p)))
+ ((ctx-error context) InvalidOperation))
- def __deepcopy__(self, memo):
- if type(self) is Decimal:
- return self # My components are also immutable
- return self.__class__(str(self))
+ (((ref self '_isinfinity))
+ (Decimal self))
+
+ ;; get values, pad if necessary
+ (let ((torot (int other))
+ (rotdig (ref self '_int))
+ (topad (- p (len rotdig))))
+
+ (cond
+ ((> topad 0)
+ (set! rotdig (+ (* "0" topad) + rotdig)))
+ ((< topad 0)
+ (set! rotdig (pylist-slice rotdig (- topad) None None)))
+ (else #f))
- # PEP 3101 support. the _localeconv keyword argument should be
- # considered private: it's provided for ease of testing only.
- def __format__(self, specifier, context=None, _localeconv=None):
- """Format a Decimal instance according to the given specifier.
+
+ ;; let's shift!
+ (let ((shifted (if (< torot 0)
+ (pylist-splice rotdig None torot None)
+ (pylist-splice (+ rotdig (* "0" torot))
+ (- p) None None))))
+
+ (_dec_from_triple (ref self '_sign)
+ (or (bool (py-lstrip shifted "0")) "0")
+ (ref self '_exp)))))))
+
+ ;; Support for pickling, copy, and deepcopy
+ ;; def __reduce__(self):
+ ;; return (self.__class__, (str(self),))
+
+ ;; def __copy__(self):
+ ;; if type(self) is Decimal:
+ ;; return self # I'm immutable; therefore I am my own clone
+ ;; return self.__class__(str(self))
+
+ ;; def __deepcopy__(self, memo):
+ ;; if type(self) is Decimal:
+ ;; return self # My components are also immutable
+ ;; return self.__class__(str(self))
+
+ ;; PEP 3101 support. the _localeconv keyword argument should be
+ ;; considered private: it's provided for ease of testing only.
+ (define __format__
+ (lam (self specifier (= context None) (= _localeconv None))
+ "Format a Decimal instance according to the given specifier.
The specifier should be a standard format specifier, with the
form described in PEP 3101. Formatting types 'e', 'E', 'f',
'F', 'g', 'G', 'n' and '%' are supported. If the formatting
type is omitted it defaults to 'g' or 'G', depending on the
value of context.capitals.
- """
+ "
- # Note: PEP 3101 says that if the type is not present then
- # there should be at least one digit after the decimal point.
- # We take the liberty of ignoring this requirement for
- # Decimal---it's presumably there to make sure that
- # format(float, '') behaves similarly to str(float).
- if context is None:
- context = getcontext()
-
- spec = _parse_format_specifier(specifier, _localeconv=_localeconv)
-
- # special values don't care about the type or precision
- if self._is_special:
- sign = _format_sign(self._sign, spec)
- body = str(self.copy_abs())
- if spec['type'] == '%':
- body += '%'
- return _format_align(sign, body, spec)
-
- # a type of None defaults to 'g' or 'G', depending on context
- if spec['type'] is None:
- spec['type'] = ['g', 'G'][context.capitals]
-
- # if type is '%', adjust exponent of self accordingly
- if spec['type'] == '%':
- self = _dec_from_triple(self._sign, self._int, self._exp+2)
-
- # round if necessary, taking rounding mode from the context
- rounding = context.rounding
- precision = spec['precision']
- if precision is not None:
- if spec['type'] in 'eE':
- self = self._round(precision+1, rounding)
- elif spec['type'] in 'fF%':
- self = self._rescale(-precision, rounding)
- elif spec['type'] in 'gG' and len(self._int) > precision:
- self = self._round(precision, rounding)
- # special case: zeros with a positive exponent can't be
- # represented in fixed point; rescale them to 0e0.
- if not self and self._exp > 0 and spec['type'] in 'fF%':
- self = self._rescale(0, rounding)
-
- # figure out placement of the decimal point
- leftdigits = self._exp + len(self._int)
- if spec['type'] in 'eE':
- if not self and precision is not None:
- dotplace = 1 - precision
- else:
- dotplace = 1
- elif spec['type'] in 'fF%':
- dotplace = leftdigits
- elif spec['type'] in 'gG':
- if self._exp <= 0 and leftdigits > -6:
- dotplace = leftdigits
- else:
- dotplace = 1
-
- # find digits before and after decimal point, and get exponent
- if dotplace < 0:
- intpart = '0'
- fracpart = '0'*(-dotplace) + self._int
- elif dotplace > len(self._int):
- intpart = self._int + '0'*(dotplace-len(self._int))
- fracpart = ''
- else:
- intpart = self._int[:dotplace] or '0'
- fracpart = self._int[dotplace:]
- exp = leftdigits-dotplace
+ ;; Note: PEP 3101 says that if the type is not present then
+ ;; there should be at least one digit after the decimal point.
+ ;; We take the liberty of ignoring this requirement for
+ ;; Decimal---it's presumably there to make sure that
+ ;; format(float, '') behaves similarly to str(float).
+ (if (eq? context None)
+ (set! context (getcontext)))
- # done with the decimal-specific stuff; hand over the rest
- # of the formatting to the _format_number function
- return _format_number(self._sign, intpart, fracpart, exp, spec)
+ (twix
+ (let ((spec
+ (_parse_format_specifier specifier
+ #:_localeconv _localeconv))))
+
+ (let ((type (pylist-ref spec "type"))))
+ ;; special values don't care about the type or precision
+ ((bool (ref self '_is_special)) it
+ (let ((sign (_format_sign (ref self '_sign) spec))
+ (body (str ((ref self 'copy_abs)))))
+ (if (equal? type "%")
+ (set! body (+ body "%")))
+ (_format_align sign body spec)))
+
+ ;; a type of None defaults to 'g' or 'G', depending on context
+ (if (eq? type None)
+ (pylist-set! spec "type"
+ (if (= (ctx-cap context) 0) "g" "G")))
+
+ (let ((type (pylist-ref spec "type"))))
+ ;; if type is '%', adjust exponent of self accordingly
+ (if (equal? type "%")
+ (set! self
+ (_dec_from_triple (ref self '_sign)
+ (ref self '_int)
+ (+ (ref self '_exp) 2))))
+
+ ;; round if necessary, taking rounding mode from the context
+ (let ((rounding (ctx-round context))
+ (precision (pylist-ref spec "precision")))
+ (if (not (eq? precision None))
+ (cond
+ ((in type "eE")
+ (set! self ((ref self '_round) (+ precision 1) rounding)))
+ ((in type "fF%")
+ (set! self ((ref self '_rescale) (- precision) rounding)))
+ ((and (in type "gG") (> (len (ref self '_int)) precision))
+ (set! self ((ref self '_round) precision rounding)))
+ (else #t)))
+
+ ;; special case: zeros with a positive exponent can't be
+ ;; represented in fixed point; rescale them to 0e0.
+ (if (and (not (bool self)) (> (ref self '_exp) 0) (in type "fF%"))
+ (set! self ((ref self '_rescale) 0 rounding))))
+
+ ;; figure out placement of the decimal point
+ (let* ((leftdigits (+ (ref self '_exp) (len (ref self '_int))))
+ (dotplace
+ (cond
+ ((in type "eE")
+ (if (and (not (bool self)) (not (eq? precision None)))
+ (- 1 precision)
+ 1))
+ ((in type "fF%")
+ leftdigits)
+ ((in type "gG")
+ (if (and (<= (ref self '_exp) 0) (> leftdigits -6))
+ leftdigits
+ 1))
+ (else
+ 1)))))
+
+ ;; find digits before and after decimal point, and get exponent
+ (call-with-values
+ (lambda ()
+ (cond
+ ((< dotplace 0)
+ (values '0'
+ (+ (* "0" (- dotplace)) (ref self '_int))))
+ ((> dotplace (len (ref self '_int)))
+ (values (+ (ref self '_int) (* "0" (- dotplace
+ (len (ref self '_int)))))
+ ""))
+ (else
+ (values
+ (or (bool (pylist-splice (ref self '_int) None dotplace None))
+ "0")
+ (pylist-splice (ref self '_int) dotplace None None)))))
+ (lambda (intpart fracpart)
+ (let ((exp (- leftdigits dotplace)))
+ ;; done with the decimal-specific stuff; hand over the rest
+ ;; of the formatting to the _format_number function
+ (_format_number (ref self '_sign) intpart fracpart exp spec))))))))
def _dec_from_triple(sign, coefficient, exponent, special=False):
"""Create a decimal instance directly, without any validation,