Maxima: Round like in Excel - maxima

Is there a function which rounds numbers (even decimal numbers) like round() in Excel?
Example
Round 1,45 to one decimal: 1,5
Round 2,45 to one decimal: 2,5
There is a similar question but they use a different algorithm.

OK, here's an attempt to reimplement Excel =ROUND function in Maxima. Some notes. (1) Values are rounded to 15 significant digits before applying the user's rounding. This is an attempt to work around problems caused by inexact representation of decimals as floating point numbers. (2) I've implemented excel_round and integer_log10 as so-called simplifying functions. That means that the calculation isn't carried out until the arguments are something that can be evaluated (in this case, when the arguments are numbers). (3) I didn't check to see what Excel =ROUND does with negative numbers -- does it round 5 upward (i.e., towards zero in this case), or away from zero? I dunno.
I've posted this solution as the little package excel_round.mac on Github. See: https://github.com/maxima-project-on-github/maxima-packages and navigate to robert-dodier/excel_round. In the interest of completeness, I've pasted the code here as well.
Here are a few examples.
(%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
Here is the code. This is the content of excel_round.mac.
/* excel_round -- round to specified number of decimal places,
* rounding termminal 5 upwards, as in MS Excel, apparently.
* Inspired by: https://stackoverflow.com/q/62533742/871096
*
* 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;

The problem of rounding numbers is actually pretty subtle, but here is a simple approach which I think gives workable results. Here I define a new function myround which has the behavior described for 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

Related

How to substitute expressions containing units when using the ezunits package?

Without specifying units, I can express area and volume and have Maxima show the relationship:
(%i1) areaNoUnits: area = width * length$
(%i2) volumeNoUnits: volume = area * height$
(%i3) volumeNoUnits, areaNoUnits;
(%o3) volume = height length width
(%i4) subst(areaNoUnits, volumeNoUnits);
(%o4) volume = height length width
Now I want to specify units so I will use the ezunits package.
The ` (backtick) operator is the building block of ezunits:
An expression a ` b represents a dimensional quantity, with a indicating a nondimensional quantity and b indicating the dimensional units.
When I add units to the area and volume expressions, evaluation and substitution do not work:
(%i1) load ("ezunits")$
(%i2) areaWithUnits: area ` m^2 = (width ` m) * (length ` m);
2 2
(%o2) area ` m = length width ` m
(%i3) volumeWithUnits: volume ` m^3 = (area ` m^2) * (height ` m);
3 3
(%o3) volume ` m = area height ` m
(%i4) volumeWithUnits, areaWithUnits;
3 3
(%o4) volume ` m = area height ` m
(%i5) subst(areaWithUnits, volumeWithUnits);
3 3
(%o5) volume ` m = area height ` m
The expected output is:
volumeWithUnits, areaWithUnits;
3 3
volume ` m = height length width ` m
I do not see a function in the ezunits package to do evaluation or substitution. What is the right way to do this?
I would phrase it like this:
(%i2) load (ezunits) $
(%i3) width: W ` m;
(%o3) W ` m
(%i4) length: L ` m;
(%o4) L ` m
(%i5) area: width * length;
2
(%o5) L W ` m
(%i6) height: H ` m;
(%o6) H ` m
(%i7) volume: area * height;
3
(%o7) H L W ` m
I wrote each part as conceptualname: symbolforquantity ` unit and then wrote just conceptualname in further calculations, instead of conceptualname ` unit.
The substitution you tried in %i5 didn't work because subst is a purely formal substitution -- if there isn't a literal subexpression which is the same as the substituted-for expression, it doesn't match; subst doesn't look for rearrangements or factorizations which could help make a match. There are ways to work around that, so it might be possible to make your original formulation work, but I think it's better overall to sidestep the problem and work with conceptualname and symbolforquantity ` unit.
To say a little about what more one could do with expressions like %o7 above. There are at least two ways to replace symbols H, L, and W with specific values. One is to call subst:
(%i2) load (ezunits) $
(%i3) volume: H*L*W ` m^3;
3
(%o3) H L W ` m
(%i4) subst ([L = 20, W = %pi], volume);
3
(%o4) 20 %pi H ` m
Another is to make use of ev.
(%i5) ev (volume, L = 20, W = %pi);
3
(%o5) 20 %pi H ` m
Note that at the input prompt, something, someflags, somevalues is equivalent to ev(something, someflags, somevalues).
(%i6) volume, L = 20, W = %pi;
3
(%o6) 20 %pi H ` m
This is just a convenience. Within a function, one has to say ev(...); the shorter syntax isn't understood there.
ev is often convenient, but it's generally simpler to predict what the result is going to be with subst instead.

cspline in Maxima giving me a result which indicates an error in Maxima

I have the following cubic polynomial f(x)=x³ - 3 x² + x -5 for which the cubic spline should provide the exact same polynomial assuming the following data:
(-1, -10), (0,-5), (1, -6) with second derivative at the extremes f''(-1)=-12, f''(1)=0 (note that f''(x)=6x-6.)
Here the piece of code that I tried on:
/* polynomial to interpolate and data */
f(x) := x^3 - 3* x^2 + x - 5$
x0:-1$
x1:0$
x2:1$
y0:f(x0)$
y1:f(x1)$
y2:f(x2)$
p:[[x0,y0],[x1,y1],[x2,y2]]$
fpp(x) := diff(f(x),x,2);
fpp0 : at( fpp(x), [x=x0]);
fpp2 : at( fpp(x), [x=x2]);
/* here I call cspline with d1=fpp0 and dn=fpp2 */
load(interpol)$
cspline(p, d1=fpp0, dn=fpp2);
I expected the original polynomial (f(x)=x³ -3 x² + x -5) but I got the result:
(%o40) (-16*x^3-15*x^2+6*x-5)*charfun2(x,-inf,0)+(8*x^3-15*x^2+6*x-5)*charfun2(x,0,inf)
which does not agrees with the original polynomial.
Evenmore. Here is a test on the results provided by Maxima.
Code:
/* verification */
h11(x) := -16*x^3 - 15* x^2 + 6* x - 5;
h22(x) := 8* x^3 - 15*x^2 + 6* x - 5;
h11pp(x) := diff(h11(x), x, 2);
h11pp0: at( h11pp(x), [x=x0]);
h22pp(x) := diff(h22(x), x, 2);
h22pp2 : at(h22pp(x), [x=x2]);
which throws 66 and 18 as the boundary conditions, which should be instead -12 and 0.
Thanks.
It appears you've misinterpreted the arguments d1 and dn for cspline. As the description of cspline says, d1 and dn specify the first derivative for the spline at the endpoints, not the second derivative.
When I use the first derivative of f to specify the values for d1 and dn, I get the expected result:
(%i2) f(x) := x^3 - 3* x^2 + x - 5$
(%i3) [x0, x1, x2]: [-1, 0, 1] $
(%i4) [y0, y1, y2]: map (f, %);
(%o4) [- 10, - 5, - 6]
(%i5) p: [[x0, y0], [x1, y1], [x2, y2]];
(%o5) [[- 1, - 10], [0, - 5], [1, - 6]]
(%i6) load (interpol) $
(%i7) cspline (p, d1 = at(diff(f(x), x), x=x0), dn = at(diff(f(x), x), x=x2));
3 2
(%o7) (x - 3 x + x - 5) charfun2(x, minf, 0)
3 2
+ (x - 3 x + x - 5) charfun2(x, 0, inf)

How do I get Maxima to store function output?

I have the logistic map function in Maxima like so:
F(x,r,n):= x[n]=r*x[n-1]*(1-x[n-1]);
And when I input the correct variables it gives me the answer to, for example, x[0]:
(%i15) n:0$
x[n-1]:[0.1]$
F(x, r:3, n);
(%o15) x[0]=[0.27]
However, this answer does not stay memorized and when I enter x[0] I get
x[0];
(%o5) x[0]
How do I write a function that will calculate x[n] for me and store it in memory, so I can use it later? I am trying to make a bifurcation diagram for the logistic map without using any black boxes, i.e., the orbits functions.
Thank you!
There are different ways to go about it. One straightforward way is to create a list and then iterate, computing its elements one by one. E.g.:
(%i4) x: makelist (0, 10);
(%o4) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
(%i5) x[1]: 0.1;
(%o5) 0.1
(%i6) r: 3;
(%o6) 3
(%i7) for i:2 thru 10 do x[i]: r * x[i - 1] * (1 - x[i - 1]);
(%o7) done
(%i8) x;
(%o8) [0.1, 0.2700000000000001, 0.5913000000000002,
0.7249929299999999, 0.5981345443500454, 0.7211088336156269,
0.603332651091411, 0.7179670896552621, 0.6074710434816448,
0.7153499244388992]
Note that : is the assignment operator, not =.

How to integrate characteristic functions

The following integral does is not evaluated by Maxima:
integrate(charfun(x<1/2), x, 0, 1);
Is there a different trick to make it work, or is it simply un-implemented?
The share package abs_integrate can integrate some expressions containing signum, abs, and unit_step. In this case you can write charfun(x < 1/2) in terms of signum(1/2 - x) and then abs_integrate can handle it.
You'll need to load abs_integrate. Note that abs_integrate modifies the behavior of integrate; there isn't a separate abs_integrate function to call.
(%i2) load (abs_integrate) $
(%i3) integrate (signum (1/2 - x), x, 0, 1);
(%o3) 0
(%i4) integrate (signum (1/2 - x), x, -1, 1);
(%o4) 1
(%i5) foo (e) := (1 + signum(e))/2;
1 + signum(e)
(%o5) foo(e) := -------------
2
(%i6) integrate (foo (1/2 - x), x, 0, 1);
1
(%o6) -
2
(%i7) integrate (foo (1/2 - x), x, -1, 1);
3
(%o7) -
2
Note that foo corresponds to charfun here.

How do I use lhs and rhs to define a function?

In the Maxima session below, how come f(1) is not 0?
(%i1) eq: 2 * x + 1 = 3;
(%o1) 2 x + 1 = 3
(%i2) f(x) := lhs(eq) - rhs(eq);
(%o2) f(x) := lhs(eq) - rhs(eq)
(%i3) f(1);
(%o3) 2 x - 2
the process of function calling in maxima here binds x to 1 in the function
definition, lhs(eq)-rhs(eq). That has no x in it, so that binding does nothing.
Next, lhs(eq) is evaluated to 2*x+1. rhs(eq) is evaluated to 3. etc.
Do you always want the same equation eq? perhaps you want to do
define(f(x),lhs(eq)-rhs(eq));
to check what the definition is, try
grind(f);
If you want to vary the equation maybe something like
g(val, eq) := subst(val,x, lhs(eq)-rhs(eq)) ;
would do.

Resources