Subject: Re: bug#15859: 24.3.50; Calc - dimensionless units again

From: Michael Heerdegen <[email-masked]>
Date: Fri, 14 Apr 2023 17:12:32 +0200

"Roland Winkler" <[email-masked]> writes:

> (2) If one wants to convert to the new unit "in / (c hr)" the result
>     returned by calc-convert-units is 2.33e-8, that is, the plain
>     number which would have been the correct result for (1).
>     The new unit "in / (c hr)" is ignored by calc-convert-units.
>     The correct result would be "992125.98 in / (c hr)".

I tried to follow what Calc is doing in this case using Edebug.
Everything seems to work as expected - it's only that Calc automatically
unit-simplifies the resulting expression, and that brings us to the
simpler unit-less form.  This happens here (I was using fraction mode
when recording this backtrace):

| 1 -> (math-simplify-units (* (frac 126000000 127) (/ (var in var-in) (* (var c var-c) (var hr var-hr))))) 16:55:53.949
|   (math-apply-units (/ (/ (* 7 (var m var-m)) (* (/ (* 299792458 (var m var-m)) (var s var-s)) (var s var-s))) (/ (* 254 (* (^ 10 -2) (* (^ 10 -2) (var m var-m)))) (* (/ (* 299792458 (var m var-m)) (var s var-s)) (* 60 (* 60 (var s var-s)))))) (/ (var in var-in) (* (var c var-c) (var hr var-hr))) nil nil)
|   (math-convert-units-rec (/ (* 7 (var m var-m)) (* (var c var-c) (var s var-s))))
|   (math-normalize (let ((calc-internal-prec (+ calc-internal-prec 2))) (let ((compat (and (not pure) (math-find-compatible-unit expr new-units))) (math-cu-unit-list nil) (math-combining-units nil)) (if compat (math-simplify-units (math-mul (math-mul (math-simplify-units ...) (math-pow new-units ...)) (math-simplify-units (math-to-standard-units ... nil)))) (if (setq math-cu-unit-list (math-decompose-units new-units)) (progn (setq new-units (nth 2 ...)))) (if (eq (car-safe expr) '+) (progn (setq expr (math-simplify-units expr)))) (if (math-units-in-expr-p expr t) (let ((math-cu-new-units new-units) (math-cu-pure pure)) (math-convert-units-rec expr)) (math-apply-units (math-to-standard-units (list ... expr new-units) nil) new-units math-cu-unit-list pure))))))
|   (math-convert-units (/ (* 7 (var m var-m)) (* (var c var-c) (var s var-s))) (/ (var in var-in) (* (var c var-c) (var hr var-hr))) nil)
|   (calc-do (closure ((new-units . "in / (c hr)") (old-units)) nil (let ((expr (calc-top-n 1)) (uoldname nil) (unitscancel nil) (nouold nil) units defunits) (if (or (not (math-units-in-expr-p expr t)) (setq unitscancel (and (if ... ... calc-allow-units-as-numbers) (eq ... 1)))) (let ((uold (or old-units ...))) (if (or calc-allow-units-as-numbers (math-units-in-expr-p uold t)) nil (error "No units specified")) (if (eq (car-safe uold) 'error) (progn (error "Bad format in units expression: %s" ...))) (setq expr (math-mul expr uold)))) (setq defunits (math-get-default-units expr)) (if new-units nil (setq new-units (read-string (format-prompt (if ... ... "New units") defunits))) (if (string= new-units "") (progn (setq new-units (or defunits "1"))))) (if (string-match "\\` */" new-units) (progn (setq new-units (concat "1" new-units)))) (setq units (math-read-expr new-units)) (if (eq (car-safe units) 'error) (progn (error "Bad format in units expression: %s" (nth 2 units)))) (if calc-ensure-consistent-units (math-check-unit-consistency expr units)) (let ((unew (math-units-in-expr-p units t)) (std (and (eq ... ...) (assq ... math-standard-units-systems))) (comp (eq (car-safe units) '+))) (if (or unew std calc-allow-units-as-numbers) nil (error "No units specified")) (let* ((noold (and uoldname ...)) (res (if std ... ...))) (if std nil (math-put-default-units (if noold units res) (if comp units))) (calc-enter-result 1 "cvun" res))))) 44)
|   (calc-convert-units)
|   (command-execute calc-convert-units)
| 1 <- math-simplify-units: (frac 1 42827494)

[ Note that special variables (`math-cu-new-units', `math-cu-unit-list',
`math-cu-pure') are being bound there by Calc so just evaluating some of
the stack expression might give you different results. ]

At least the result given by Calc is not incorrect.  It would be better
if it would respect the units explicitly requested by the user, though.

Michael.