Matlab syms variable precision? - matlab

Just having some issues with variable precision in matlab..
I have the code:
x = 0.1;
syms y;
S = solve(x+1==(1+y)/(1-y),y);
y = double(S);
val = abs(((2^2)*(y^2))/(2*(y-1)^2))
But val is always rounded off.
I should be getting val = 0.0049999 but instead I am getting val = 0.0050.
Anyone have any idea why?
Thanks.
EDIT: Adding extra code
x = 0.1;
syms y;
S = solve(x+1==(1+y)/(1-y),y);
y = double(S);
val = abs(((2^2)*(y^2))/(2*(y-1)^2))
sprintf('%22.20f',val)
for i=1:2
val= val+((2^i)*(y^i))/(i*(y-1)^i);
sprintf('%22.20f',val)
end
The first sprintf works and shows correct rounding, however the second doesnt!!

It has to do with the floating-point representation and how Matlab displays such numbers for readability. If you add this line to the end of your code:
sprintf('%22.20f',val)
you'll get:
ans =
0.00499999999999999924
Edit
Even though it technically deals with Python, this website offers a brief and concise overview on the limitations of floating-point representations.

Is it possible that you merely set your preferences for displaying numbers to short rather than long? It would help if you eliminated that as a possibility.

Related

How to solve a matlab fit?

So I want to solve a plot fit for several points, but the problem, that I am facing right now is, that my fit object fit_eq is a char but solve needs a sym. I searched everywhere and couldn't find a solution how to fix this. Here is my code, I cut unimportant parts, and some variables are german words, so don't get confused. gesamt is a 60x20 matrix, where every odd column is the same (it's made out of 10 matrices which are an outcome of a meassurement).
anzahlproben = 10;
for i = 1:2:anzahlproben*2
probe = gesamt(:,i:i+1);
[row c]=find(probe==0);
row(1:2,:)=[];
for j=row
probe(j,:)=[];
end
N22_{(i+1)/2} = probe;
end
for i = 1:1:anzahlproben
x = N22_{i}(1:1:size(N22_{i},1),1);
y = N22_{i}(1:1:size(N22_{i},1),2);
ft = fittype('poly9');
fitobject_{i}=fit(x,y,ft);
end
cvalues = coeffvalues(fitobject_{1});
cnames = coeffnames(fitobject_{1});
fit_eq = formula(fitobject_{1});
for ii=1:1:numel(cvalues)
cname = cnames{ii};
cvalue = num2str(cvalues(ii));
fit_eq = strrep(fit_eq, cname , cvalue);
end
y=1;
syms x
erg = (solve(fit_eq == y,x))
I got the last part from here and gives an equation in a char.
Matlab gives the output:
erg =
Empty sym: 0-by-1
Which can't be right. Any ideas?
As mentioned in the first line of the doc:
Support for character vector or string inputs has been removed.
Instead, use syms to declare variables and replace inputs such as
solve('2*x == 1','x') with solve(2*x == 1,x).
So eqn should be of class sym not a string!
Try:
erg = solve(str2sym(fit_eq) == y,x)
If it still don't work either your equation is wrong or you've not declared the symbolic variables involved in your equation.

Double integral expressed by a second variable

I'm having trouble with implementing double integral in Matlab.
Unlike other double integrals, I need the result of the first (inside) integral to be an expression of the second variable, before going through the second (outside) integral, as it must be powered by k.
For example:
In the example above, I need the result of the inside integral to be expressed as 2y, so that I can calculate (2y)^k, before doing the second (outside) integral.
Does anyone know how to do this in Matlab?
I don't like doing things symbolically, because 99.9% of all problems don't have a closed form solution at all. For 99.9% of the problems that do have a closed-form solution, that solution is unwieldy and hardly useful at all. That may be because of my specific discipline, but I'm going to assume that your problem falls in one of those 99.9% sets, so I'll present the most obvious numerical way to do this.
And that is, integrate a function which calls integral itself:
function dbl_int()
f = #(x,y) 2.*x.*y + 1;
k = 1;
x_limits = [0 1];
y_limits = [1 2];
val = integral(#(y) integrand(f, y, k, x_limits), ...
y_limits(1), y_limits(2));
end
function val = integrand(f, y, k, x_limits)
val = zeros(size(y));
for ii = 1:numel(y)
val(ii) = integral(#(x) f(x,y(ii)), ...
x_limits(1), x_limits(2));
end
val = val.^k;
end

Matlab logncdf function is not producing expected result

So on this problem it seems pretty straight forward we are given
mean of x = 10,281 and sigma of x = 4112.4
We are asked to determine P(X<15,000)
Now I thought the code for this in matlab should be super straightforward
mu = 10281
sigma = 4112.4
p = logncdf(15000,10281,4112.4)
However this gives
p = .0063
The given answer is .8790 and just looking at p you can tell it is wrong because we are at 15000 which is over the mean which means it should be above .5. What is the deal with this function?
I saw somewhere you might need to take the exp(15000) for x in the function that results in a probability of 1 which is too high.
Any pointers would be much appreciated
%If X is lognormally distributed with parameters:-
mu = 10281;
sigma = 4112.4;
%then log(X) is normally distributed with following parameters:
mew_actual = log((mu^2)/sqrt(sigma^2+mu^2));
sigma_actual = sqrt(log((sigma^2)/(mu^2) +1));
Now you can use either of the following to compute CDF:-
p = cdf('Normal',log(15000),mew_actual,sigma_actual)
or
p=logncdf(15000,mew_actual,sigma_actual)
which gives 0.8796
(which I believe is the correct answer)
The answer given to you is 0.8790 because if you solve the question by hand, you'll get something like: z = 1.172759 and when you look this value in the table, you can only find z = 1.17(without the rest of decimal places) and for which φ(z)=0.8790.
You can verify the exact answer using this calculator. The related screenshot is attached below:

Benchmark integral vs. Double integral procedure

I've got a question about the implementation of a double integral in MATLAB.
It's known that
Making use of
k1 = 1E-04:0.001:1E+04;
k2 = 1E-04:0.001:1E+04;
k3 = 1E-04:0.001:1E+04;
the above procedure(calling the formula of F11,F22 and F33) leads to the results shown below:
Now comes the question:
I would like to achieve the same results by means of a double integral (or nested single integral in k2 and k3) involving only phi11,phi22 and phi33, hence without calling directly the formula for F11, F22 and F33.
Up to now I'm using this code (restricted to F11 calculation):
clc,clear all,close all
k1 = (1E-04:0.01:10000);
k2 = (1E-04:0.01:10000);
k3 = (1E-04:0.01:10000);
F_11_benchmark = ((9/55)*1.453)*(1./(1+k1.^2).^(5/6));
count = 0;
F_11 = zeros(1,numel(k1));
for i = 2:numel(k1)
count = count + 1;
phi_11 = #(k2,k3) (1.453./(4.*pi)).*((k2.^2+k3.^2)./((1 + k1(count).^2 + k2.^2+k3.^2).^(17/6)));
F_11(count) = dblquad(phi_11,k2(i-1),k2(i),k3(i-1),k3(i));
end
not leading to the same benchmark results shown in the last figure.
How would you suggest to tackle this problem? I thank you in advance.
Best regards,
FPE
You are using dblquad wrong. The way you are doing it calculates for each k1 the integral over the little box [k1(i-1) k1(i)]x[k1(i-1) k1(i)], which in no way approximates the whole domain of integration. Use the integral2 command instead: try just using
F_11(count) = integral2(phi_11,-100,100,-100,100);
or
F_11(count) = integral2(phi_11,-Inf,Inf,-Inf,Inf);
which should be more accurate but will be slower. You can probably also get away with using a much larger resolution for k1. I used
k1=10.^(-4:.01:3);
to sort of match your graph.

Matlab Code To Approximate The Exponential Function

Does anyone know how to make the following Matlab code approximate the exponential function more accurately when dealing with large and negative real numbers?
For example when x = 1, the code works well, when x = -100, it returns an answer of 8.7364e+31 when it should be closer to 3.7201e-44.
The code is as follows:
s=1
a=1;
y=1;
for k=1:40
a=a/k;
y=y*x;
s=s+a*y;
end
s
Any assistance is appreciated, cheers.
EDIT:
Ok so the question is as follows:
Which mathematical function does this code approximate? (I say the exponential function.) Does it work when x = 1? (Yes.) Unfortunately, using this when x = -100 produces the answer s = 8.7364e+31. Your colleague believes that there is a silly bug in the program, and asks for your assistance. Explain the behaviour carefully and give a simple fix which produces a better result. [You must suggest a modification to the above code, or it's use. You must also check your simple fix works.]
So I somewhat understand that the problem surrounds large numbers when there is 16 (or more) orders of magnitude between terms, precision is lost, but the solution eludes me.
Thanks
EDIT:
So in the end I went with this:
s = 1;
x = -100;
a = 1;
y = 1;
x1 = 1;
for k=1:40
x1 = x/10;
a = a/k;
y = y*x1;
s = s + a*y;
end
s = s^10;
s
Not sure if it's completely correct but it returns some good approximations.
exp(-100) = 3.720075976020836e-044
s = 3.722053303838800e-044
After further analysis (and unfortunately submitting the assignment), I realised increasing the number of iterations, and thus increasing terms, further improves efficiency. In fact the following was even more efficient:
s = 1;
x = -100;
a = 1;
y = 1;
x1 = 1;
for k=1:200
x1 = x/200;
a = a/k;
y = y*x1;
s = s + a*y;
end
s = s^200;
s
Which gives:
exp(-100) = 3.720075976020836e-044
s = 3.720075976020701e-044
As John points out in a comment, you have an error inside the loop. The y = y*k line does not do what you need. Look more carefully at the terms in the series for exp(x).
Anyway, I assume this is why you have been given this homework assignment, to learn that series like this don't converge very well for large values. Instead, you should consider how to do range reduction.
For example, can you use the identity
exp(x+y) = exp(x)*exp(y)
to your advantage? Suppose you store the value of exp(1) = 2.7182818284590452353...
Now, if I were to ask you to compute the value of exp(1.3), how would you use the above information?
exp(1.3) = exp(1)*exp(0.3)
But we KNOW the value of exp(1) already. In fact, with a little thought, this will let you reduce the range for an exponential down to needing the series to converge rapidly only for abs(x) <= 0.5.
Edit: There is a second way one can do range reduction using a variation of the same identity.
exp(x) = exp(x/2)*exp(x/2) = exp(x/2)^2
Thus, suppose you wish to compute the exponential of large number, perhaps 12.8. Getting this to converge acceptably fast will take many terms in the simple series, and there will be a great deal of subtractive cancellation happening, so you won't get good accuracy anyway. However, if we recognize that
12.8 = 2*6.4 = 2*2*3.2 = ... = 16*0.8
then IF you could efficiently compute the exponential of 0.8, then the desired value is easy to recover, perhaps by repeated squaring.
exp(12.8)
ans =
362217.449611248
a = exp(0.8)
a =
2.22554092849247
a = a*a;
a = a*a;
a = a*a;
a = a*a
362217.449611249
exp(0.8)^16
ans =
362217.449611249
Note that WHENEVER you do range reduction using methods like this, while you may incur numerical problems due to the additional computations necessary, you will usually come out way ahead due to the greatly enhanced convergence of your series.
Why do you think that's the wrong answer? Look at the last term of that sequence, and it's size, and tell me why you expect you should have an answer that's close to 0.
My original answer stated that roundoff error was the problem. That will be a problem with this basic approach, but why do you think 40 is enough terms for the appropriate mathematical ( as opposed to computer floating point arithmetic) answer.
100^40 / 40! ~= 10^31.
Woodchip has the right idea with range reduction. That's the typical approach people use to implement these kinds of functions very quickly. Once you get that all figured out, you deal with roundoff errors of alternating sequences, by summing adjacent terms within the loop, and stepping with k = 1 : 2 : 40 (for instance). That doesn't work here until you use woodchips's idea because for x = -100, the summands grow for a very long time. You need |x| < 1 to guarantee intermediate terms are shrinking, and thus a rewrite will work.