+\f
+;;;; TRUNCATE, FLOOR, CEILING, and ROUND
+
+(macrolet ((define-frobs (fun ufun)
+ `(progn
+ (defknown ,ufun (real) integer (movable foldable flushable))
+ (deftransform ,fun ((x &optional by)
+ (* &optional
+ (constant-arg (member 1))))
+ '(let ((res (,ufun x)))
+ (values res (- x res)))))))
+ (define-frobs truncate %unary-truncate)
+ (define-frobs round %unary-round))
+
+(deftransform %unary-truncate ((x) (single-float))
+ `(%unary-truncate/single-float x))
+(deftransform %unary-truncate ((x) (double-float))
+ `(%unary-truncate/double-float x))
+
+;;; Convert (TRUNCATE x y) to the obvious implementation.
+;;;
+;;; ...plus hair: Insert explicit coercions to appropriate float types: Python
+;;; is reluctant it generate explicit integer->float coercions due to
+;;; precision issues (see SAFE-SINGLE-COERCION-P &co), but this is not an
+;;; issue here as there is no DERIVE-TYPE optimizer on specialized versions of
+;;; %UNARY-TRUNCATE, so the derived type of TRUNCATE remains the best we can
+;;; do here -- which is fine. Also take care not to add unnecassary division
+;;; or multiplication by 1, since we are not able to always eliminate them,
+;;; depending on FLOAT-ACCURACY. Finally, leave out the secondary value when
+;;; we know it is unused: COERCE is not flushable.
+(macrolet ((def (type other-float-arg-types)
+ (let ((unary (symbolicate "%UNARY-TRUNCATE/" type))
+ (coerce (symbolicate "%" type)))
+ `(deftransform truncate ((x &optional y)
+ (,type
+ &optional (or ,type ,@other-float-arg-types integer))
+ * :result result)
+ (let* ((result-type (and result
+ (lvar-derived-type result)))
+ (compute-all (and (values-type-p result-type)
+ (not (type-single-value-p result-type)))))
+ (if (or (not y)
+ (and (constant-lvar-p y) (= 1 (lvar-value y))))
+ (if compute-all
+ `(let ((res (,',unary x)))
+ (values res (- x (,',coerce res))))
+ `(let ((res (,',unary x)))
+ ;; Dummy secondary value!
+ (values res x)))
+ (if compute-all
+ `(let* ((f (,',coerce y))
+ (res (,',unary (/ x f))))
+ (values res (- x (* f (,',coerce res)))))
+ `(let* ((f (,',coerce y))
+ (res (,',unary (/ x f))))
+ ;; Dummy secondary value!
+ (values res x)))))))))
+ (def single-float ())
+ (def double-float (single-float)))
+
+(deftransform floor ((number &optional divisor)
+ (float &optional (or integer float)))
+ (let ((defaulted-divisor (if divisor 'divisor 1)))
+ `(multiple-value-bind (tru rem) (truncate number ,defaulted-divisor)
+ (if (and (not (zerop rem))
+ (if (minusp ,defaulted-divisor)
+ (plusp number)
+ (minusp number)))
+ (values (1- tru) (+ rem ,defaulted-divisor))
+ (values tru rem)))))
+
+(deftransform ceiling ((number &optional divisor)
+ (float &optional (or integer float)))
+ (let ((defaulted-divisor (if divisor 'divisor 1)))
+ `(multiple-value-bind (tru rem) (truncate number ,defaulted-divisor)
+ (if (and (not (zerop rem))
+ (if (minusp ,defaulted-divisor)
+ (minusp number)
+ (plusp number)))
+ (values (1+ tru) (- rem ,defaulted-divisor))
+ (values tru rem)))))
+
+(defknown %unary-ftruncate (real) float (movable foldable flushable))
+(defknown %unary-ftruncate/single (single-float) single-float
+ (movable foldable flushable))
+(defknown %unary-ftruncate/double (double-float) double-float
+ (movable foldable flushable))
+
+(defun %unary-ftruncate/single (x)
+ (declare (type single-float x))
+ (declare (optimize speed (safety 0)))
+ (let* ((bits (single-float-bits x))
+ (exp (ldb sb!vm:single-float-exponent-byte bits))
+ (biased (the single-float-exponent
+ (- exp sb!vm:single-float-bias))))
+ (declare (type (signed-byte 32) bits))
+ (cond
+ ((= exp sb!vm:single-float-normal-exponent-max) x)
+ ((<= biased 0) (* x 0f0))
+ ((>= biased (float-digits x)) x)
+ (t
+ (let ((frac-bits (- (float-digits x) biased)))
+ (setf bits (logandc2 bits (- (ash 1 frac-bits) 1)))
+ (make-single-float bits))))))
+
+(defun %unary-ftruncate/double (x)
+ (declare (type double-float x))
+ (declare (optimize speed (safety 0)))
+ (let* ((high (double-float-high-bits x))
+ (low (double-float-low-bits x))
+ (exp (ldb sb!vm:double-float-exponent-byte high))
+ (biased (the double-float-exponent
+ (- exp sb!vm:double-float-bias))))
+ (declare (type (signed-byte 32) high)
+ (type (unsigned-byte 32) low))
+ (cond
+ ((= exp sb!vm:double-float-normal-exponent-max) x)
+ ((<= biased 0) (* x 0d0))
+ ((>= biased (float-digits x)) x)
+ (t
+ (let ((frac-bits (- (float-digits x) biased)))
+ (cond ((< frac-bits 32)
+ (setf low (logandc2 low (- (ash 1 frac-bits) 1))))
+ (t
+ (setf low 0)
+ (setf high (logandc2 high (- (ash 1 (- frac-bits 32)) 1)))))
+ (make-double-float high low))))))
+
+(macrolet
+ ((def (float-type fun)
+ `(deftransform %unary-ftruncate ((x) (,float-type))
+ '(,fun x))))
+ (def single-float %unary-ftruncate/single)
+ (def double-float %unary-ftruncate/double))