Multiply two variables in Matlab with vpa - high precision - matlab

I want to be sure that two variables, a and b, are multiplied with high precision, i.e., perform the product c of a and b with arbitrary precision (in my case 50 correct decimal digits):
a = vpa(10/3,50);
b = vpa(7/13,50);
c = eval(vpa(vpa(a,50)*vpa(b,50),50)); % I basically want to do just c = a*b;
which gives me
a = 3.3333333333333333333333333333333333333333333333333
b = 0.53846153846153846153846153846153846153846153846154
c = 23.333333333333333333333333333333
Testing
d = eval(vpa(c*13,50))
gives
d = 23.333333333333333333333333333333333333335292490585
which shows that the multiplication to get c was not carried out with 50 significant digits.
What's wrong here, but, more importantly, how do I get a correct result for a*b and for other operations such as exp?

First, should use vpa('7/13',50) or vpa(7,50)/13 to avoid the possibility of losing precision dues to 7/13 being calculated in double precision floating point (I believe that vpa, like sym, tries to guess common constants and rational fractions, but you shouldn't rely on it).
The issue is that while a and b are stored as 50-digit variable precision values, your multiplication is still being performed according to the default value of digits (32). The second argument to vpa only appears to specify the precision of the variable, not any subsequent operations on or with it (the documentation is not particularly helpful in this respect).
One way to accomplish what you want would be:
old_digits = digits(50);
a = vpa('10/3')
b = vpa('7/13')
c = a*b
d = 13*c
digits(old_digits);
Another would be to use exact symbolic expressions for all of the math (potentially more expensive) and then convert the result to 50-digit variable precision at the end:
a = sym('10/3')
b = sym('7/13')
c = a*b
d = vpa(13*c,50)
Both methods return 23.333333333333333333333333333333333333333333333333 for d.

Related

How do I prevent precision loss in table values?

I currently have the following code:
count = 20;
n = zeros(count, 1);
P = zeros(count, 1);
for i = 1:count
n(i) = i;
P(i) = sym(i)^i + (sym(1-i))^(i-1);
if i == (count)
T = table(n,P)
end
end
This gives me a table with a set of values. However, some of the values are losing precision because they have too many digits.
I am aware that MATLAB allows for up to 536870913 digits of precision. How do I make my table values not lose precision?
Note: if I were to just do the following operation (for example): sym(200)^2010, I would get the full precision value, which has 4626 digits. Doing this for table values doesn't seem to work, though, for some strange reason.
If someone could please help me with this I would be extremely grateful as I have been struggling with this for several hours now.
As #Daniel commented, the problem is that you are casting to double when storing it in P. MATLAB only has the precision you mention when using symbolic variables, but when you get into the numerical world, you can only store a finite amount of precision.
To be exact, once you define P as a double (zeros returns a double vector), the biggest integer you can store such that all of its smaller integers are precise is 2^53, way smaller than your P(20). This means that any integer bigger than 2^53 is not ensured to be precise on a double valued vector.
Your solution is thus to avoid casting, to store the variable on a sym type P. Note that the above also applies to later maths. If you plan to use this variable in some equation, remember that when you pass it to numerical form, you will lose precision. Often this does not matter, as the precision lost is very small, but you should know it.
If you want to learn more about how numerical precision work on computers, I suggest reading the following Q&A: Why is 24.0000 not equal to 24.0000 in MATLAB?
Sym solution:
count = 20;
n = zeros(count, 1);
P = sym('integer',[count, 1]);
for i = 1:count
n(i) = i;
P(i) = sym(i)^i + (sym(1-i))^(i-1);
if i == (count)
T = table(n,P)
end
end
returns
>> T.P(20)
ans =
102879180344339686410876021

MATLAB: How to discard overflow digits binary addition?

I want to know if it's possible in MATLAB to discard overflow digits in MATLAB when I add two binary numbers.
I've only been able to find how to have a least number of binary digits, but how to I set a maximum number of digits?
For example:
e = dec2bin(bin2dec('1001') + bin2dec('1000'))
That gave me:
e =
10001
How do I get only '0001'?
dec2bin will always give you the minimum amount of bits to represent a number. If you would like to retain the n least significant digits, you have to index into the string and grab those yourself.
Specifically, if you want to retain only the n least significant digits, given that you have a base-10 number stored in num, you would do this:
out = dec2bin(num);
out = out(end-n+1:end);
Bear in mind that this performs no error checking. Should n be larger than the total number of bits in the string, you will get an out of bounds error. I'm assuming you're smart enough to use this and know what you're doing.
So for your example:
e = dec2bin(bin2dec('1001') + bin2dec('1000'));
n = 4, and so:
>> n = 4;
>> e = e(end-n+1:end)
e =
0001
Here is a more robust (but less efficient, I fear) way: What you describe is exactly the modulo operation. A 4-bit binary number is the remainder after a division by 0b10000 = 16. This can be done using the mod function in MATLAB.
>> e = dec2bin(mod(bin2dec('1001') + bin2dec('1000'),16),4)
e =
0001
Note: I added 4 as additional argument to the dec2bin function, so the output will always be 4-bit wide.
This can of course be generalized to any bit width: If you want to add 8-bit numbers, you will need the remainder of the division by 0b1'0000'0000 = 256, for example
>> e = dec2bin(mod(bin2dec('10011001') + bin2dec('10001000'),256),8)
e =
00100001
Or for shorter numbers, e.g. 2-bit wide, it is 0b100 = 4:
>> e = dec2bin(mod(bin2dec('10') + bin2dec('11'),4),2)
e =
01

Increase Hex2dec or dec2hex output range in Matlab

I have a strange problem with hex2dec function in Matlab.
I realized in 16bytes data, it omits 2 LSB bytes.
hex2dec('123123123123123A');
dec2hex(ans)
Warning: At least one of the input numbers is larger than the largest integer-valued floating-point
number (2^52). Results may be unpredictable.
ans =
1231231231231200
I am using this in Simulink. Therefore I cannot process 16byte data. Simulink interpret this as a 14byte + '00'.
You need to use uint64 to store that value:
A='123123123123123A';
B=bitshift(uint64(hex2dec(A(1:end-8))),32)+uint64(hex2dec(A(end-7:end)))
which returns
B =
1310867527582290490
An alternative way in MATLAB using typecast:
>> A = '123123123123123A';
>> B = typecast(uint32(hex2dec([A(9:end);A(1:8)])), 'uint64')
B =
1310867527582290490
And the reverse in the opposite direction:
>> AA = dec2hex(typecast(B,'uint32'));
>> AA = [AA(2,:) AA(1,:)]
AA =
123123123123123A
The idea is to treat the 64-integer as two 32-bit integers.
That said, Simulink does not support int64 and uint64 types as others have already noted..

Operations with large numbers

I have some numbers a_i (for i=1 to 10000).
I need to compute exp(a_i)/sum(exp(a_j)) using matlab.
Of course, it is impossible to calculate straight away. I found some tricks, the most interesting being:
"Suppose we want to find exp(7.0873e002). This will be a large number indeed but still just barely within matlab's capability of direct calculation. However, we can find the separate exponent and mantissa without calling on 'exp' as follows;
a = 7.0873e2;
x = a/log(10);
D = floor(x); % D will be an integer
F = 10^(x-D); % F will lie in 1 <= F < 10
Then D will be the power of ten and F the mantissa
F = 6.27376373225551 % The mantissa
D = 307 % The exponent (power of ten)
Compare that with the direct answer:
exp(a) = 6.273763732256170e+307"
I tried something similar, but the result in may case is Inf:
a = 7.0873e5;
x = a/log(10);
D = floor(x);
F = 10^(x-D);
exp(a) = Inf
Anyone has an idea?
Your answer is in F and D. Because your a is much larger than the example a (i.e. e5 vs e2) which they state is just barely within Matlab's range, yours must be well out of the range and thus becomes inf. But it doesn't matter because D and F hold your answer, you aren't supposed to be checkin g it against exp(a), the example only calculates exp(a) to demonstrate the proof of concept. But the whole point of this code is to give you a way to find exp of giant numbers.
In your case you get
D =
307797
and
F =
3.374110424643062 % Use format long
thus your answer is 3.374110424643062e+307797

MATLAB - floor question

I'm a MATLAB beginner. Here's the problem:
>> a = floor(7/2.5)
a =
2.00
>> b = rem(7,2.5)
b =
2.00
>> c = floor(b/2)
c =
0
c should be 1, right? Why is it 0???
It is different when b = 2 is entered directly as follows:
>> b = 2
b =
2.00
>> c = floor(b/2)
c =
1.00
In two words: truncation errors.
You're right, c should be 1.0 in exact arithmetic. However, since you used a float in the arguments of rem, you get the answer as a float. Apparently, b is not exactly 2, but 2.0, which means that it is a double very close to 2. Therefore, b/2 becomes the double 1.0, apparently in this case its value is slightly less than one, giving you a 0 as the integer value. If you want to prevent this, use both floor and ceil, and compare the values.
If you want to convert the answer to integer, just use round instead of floor.
If you add the line
d = b-a
to your example you'll see the result
d =
-4.4409e-016
meaning Matlab calculated a number close to, but not exactly, 2 for b. This comes up quite a bit in working with floating point numbers. Try
help eps
for more information.
Numerical issues of this sort are also dealt with in the MATLAB FAQ
Yeah that is a numerical issue. You should use such things with care. If you want exact arithmetic , you should try 'sym' for your number e.g.
b=rem(sym(7),sym(2.5))
Then you will not any such errors, but your computations will be much slower.