千里马:像 Excel 中的圆形

Maxima: Round like in Excel

是否有像 Excel 中的 round() 那样对数字(甚至十进制数)进行四舍五入的函数?

例子

将 1,45 舍入到一位小数:1,5

将 2,45 舍入到一位小数:2,5

有一个 similar question 但他们使用不同的算法。

四舍五入的问题实际上非常微妙,但我认为这里有一个简单的方法可以提供可行的结果。我在这里定义了一个新函数 myround,它具有 Excel =ROUND 所描述的行为。 [1]

(%i4) myround (x, n) := round(x*10^n)/10.0^n;
                                                    n
                                         'round(x 10 )
(%o4)                   myround(x, n) := -------------
                                                 n
                                             10.0
(%i5) myround (2.15, 1);
(%o5)                                 2.2
(%i6) myround (2.149, 1);
(%o6)                                 2.1
(%i7) myround (-1.475, 2);
(%o7)                               - 1.48
(%i8) myround (21.5, -1);
(%o8)                                20.0
(%i9) myround (626.3, -3);
(%o9)                               1000.0
(%i10) myround (1.98, -1);
(%o10)                                0.0
(%i11) myround (-50.55, -2);
(%o11)                              - 100.0

[1] https://support.microsoft.com/en-us/office/round-function-c018c5d8-40fb-4053-90b1-b3e7f61a213c

好的,这是在 Maxima 中重新实现 Excel =ROUND 函数的尝试。一些笔记。 (1) 在应用用户舍入之前,值四舍五入到 15 位有效数字。这是为了解决因小数不准确表示为浮点数而导致的问题。 (2) 我已经将 excel_roundinteger_log10 实现为所谓的简化函数。这意味着直到参数是可以评估的东西(在这种情况下,当参数是数字时)才会执行计算。 (3) 我没有检查 Excel =ROUND 对负数的作用——它是向上舍入 5(即在这种情况下朝零)还是远离零?我不知道。

我已将此解决方案作为小包 excel_round.mac 发布在 Github 上。请参阅:https://github.com/maxima-project-on-github/maxima-packages 并导航至 robert-dodier/excel_round。为了完整起见,我也在这里粘贴了代码。

这里有几个例子。

(%i1) excel_round (1.15, 1);
(%o1)                                 1.2
(%i2) excel_round (1.25, 1);
(%o2)                                 1.3
(%i3) excel_round (12.455, 2);
(%o3)                                12.46
(%i4) excel_round (x, 2);
(%o4)                          excel_round(x, 2)
(%i5) ev (%, x = 9.865);
(%o5)                                9.87

这是代码。这是excel_round.mac.

的内容
/* excel_round -- round to specified number of decimal places,
 * rounding termminal 5 upwards, as in MS Excel, apparently.
 * Inspired by: 
 *
 * copyright 2020 by Robert Dodier
 * I release this work under terms of the GNU General Public License.
 */

matchdeclare (xx, numberp);
matchdeclare (nn, integerp);
tellsimpafter (excel_round (xx, nn), excel_round_numerical (xx, nn));

matchdeclare (xx, lambda ([e], block ([v: ev (e, numer)], numberp(v))));
tellsimpafter (excel_round (xx, nn), excel_round_numerical (ev (xx, numer), nn));

excel_round_numerical (x, n) :=
  block ([r, r1, r2, l],
         /* rationalize returns exact rational equivalent of float */
         r: rationalize (x),
         /* First round to 15 significant decimal places.
          * This is a heuristic to recover what a user "meant"
          * to type in, since many decimal numbers are not
          * exactly representable as floats.
          */
         l: integer_log10 (abs (r)),
         r1: round (r*10^(15 - l)),
         /* Now begin rounding to n places. */
         r2: r1/10^((15 - l) - n),
         /* If terminal digit is 5, then r2 is integer + 1/2.
          * If that's the case, round upwards and rescale,
          * otherwise, terminal digit is something other than 5,
          * round to nearest integer and rescale.
          */
         if equal (r2 - floor(r2), 1/2)
           then ceiling(r2)/10.0^n
           else round(r2)/10.0^n);

matchdeclare (xx, lambda ([e], numberp(e) and e > 0));
tellsimpafter (integer_log10 (xx), integer_log10_numerical (xx));

matchdeclare (xx, lambda ([e], block ([v: ev (e, numer)], numberp(v) and v > 0)));
tellsimpafter (integer_log10 (xx), integer_log10_numerical (ev (xx, numer)));

matchdeclare (xx, lambda ([e], not atom(e) and op(e) = "/" and numberp (denom (e)) and pow10p (denom (e))));
pow10p (e) := integerp(e) and  e > 1 and (e = 10 or pow10p (e/10));
tellsimpafter (integer_log10 (xx), integer_log10 (num (xx)) - integer_log10_numerical (denom (xx)));

integer_log10_numerical (x) :=
  if x >= 10
    then (for i from 0 do
              if x >= 10 then x:x/10 else return(i))
  elseif x < 1
    then (for i from 0 do
              if x < 1 then x:x*10 else return(-i))
  else 0;