Get the first significant digits of a number using variable precision arithmetic

Suppose you want to know the first Wsignificant digits of a number, say pi, using . A simple call with so many digits does not work. Consider the following example with : vpavpaW = 35

>> disp(vpa(sym('pi'), 35))
3.1415926535897932384626433832795029

The reason this doesn't work is rounding . In particular, the above result seems to indicate that the 35-th of the significant digits of the number pi is nine, while in fact this is the number eight that has been rounded:

>> disp(vpa(sym('pi'), 36))
3.14159265358979323846264338327950288

From the above it may seem that the solution is to ask for one additional decimal place and discard it so that the last surviving decimal place does not have problems with rounding. But this also does not work at all, because rounding can cause a carry . See this example in Matlab:

>> disp(vpa(sym('pi'), 79))
3.141592653589793238462643383279502884197169399375105820974944592307816406286209
>> disp(vpa(sym('pi'), 80))
3.141592653589793238462643383279502884197169399375105820974944592307816406286209
>> disp(vpa(sym('pi'), 81))
3.141592653589793238462643383279502884197169399375105820974944592307816406286209
>> disp(vpa(sym('pi'), 82))
3.141592653589793238462643383279502884197169399375105820974944592307816406286208999
>> disp(vpa(sym('pi'), 83))
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986

or in octave:

>> disp(vpa(sym('pi'), 79))
3.141592653589793238462643383279502884197169399375105820974944592307816406286209
>> disp(vpa(sym('pi'), 80))
3.1415926535897932384626433832795028841971693993751058209749445923078164062862090
>> disp(vpa(sym('pi'), 81))
3.14159265358979323846264338327950288419716939937510582097494459230781640628620900
>> disp(vpa(sym('pi'), 82))
3.141592653589793238462643383279502884197169399375105820974944592307816406286208999
>> disp(vpa(sym('pi'), 83))
3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986

As you can see

  • Increasing the number of decimal places vpafrom 79to 80or 81to vpagives the same answer in Matlab, because when rounding and wrapping the last digits are zero, and Matlab truncates trailing zeros.
  • Octave is not clipped, so it shows those zeros, but they are still incorrect.

, Matlab, Octave, 79 , .


,

  • vpa - ;
  • ;
  • , , . , .

, W , , ?

+5
2

-, , vpa . " ", " " :

>> disp(vpa(sym('0.135'),2))
0.14
>> disp(vpa(sym('0.125'),2))
0.12
>> disp(vpa(sym('0.115'),2))
0.11

Octave Matlab:

>> disp(vpa(sym('0.135'),2))
0.14
>> disp(vpa(sym('0.125'),2))
0.13
>> disp(vpa(sym('0.115'),2))
0.11

, - .


W . . N (, ) , A = 1 , , , A = 0 . .

enter image description here

. W = 3, Matlab.

  1. N = 0, A = 0: .

    >> disp(vpa(sym('0.12345'),3)) % works: first 3 digits are correct
    0.123
    
  2. N = 0, A = 1: , W = 3 :

    >> disp(vpa(sym('0.12378'),3)) % doesn't work
    0.124
    >> disp(vpa(sym('0.12378'),4)) % works: first 3 digits are correct
    0.1238
    
  3. N > 0, A = 0: N :

    >> disp(vpa(sym('0.123994'),3)) % doesn't work
    0.124
    >> disp(vpa(sym('0.123994'),4)) % doesn't work
    0.124
    >> disp(vpa(sym('0.123994'),5)) % works: first 3 digits are correct
    0.12399
    
  4. N > 0, A = 1: N+1 :

    >> disp(vpa(sym('0.123997'),3)) % doesn't work
    0.124
    >> disp(vpa(sym('0.123997'),4)) % doesn't work
    0.124
    >> disp(vpa(sym('0.123997'),5)) % doesn't work
    0.124
    >> disp(vpa(sym('0.123997'),6)) % works: first 3 digits are correct
    0.123997
    

E , vpa vpa , W . E = N + A

N A . , , E = 1 E 0. , E . E = max(1, N+A); , , , ( 1 ).

, , , . ( ).

s = sym('pi'); % number in symbolic form
W = 79; % number of wanted digits
E = 0; % initiallize
done = false;
while ~done
    E = E+1;
    x = char(vpa(s, W+E));
    y = regexprep(x, '^[+-]?0*|\.0*', ''); % remove sign, leading zeros,
        % decimal point and zeros right after the point; if present
    y = regexprep(y, '\D.*$', ''); % remove exponent and imaginary unit,
        % if present
    num_digits = numel(y); % get number of significant digits in x: 
    done = num_digits==W+E && x(end)~='0'; % the second condition is only
        % required in Octave, but it doesn't harm to keep it in Matlab too
end
c = find(~ismember(x, ['0':'9' '+-.']), 1);
if c % there is an exponent or/and imaginary unit
    result = [x(1:c-1-E) x(c:end)]; % discard last E digits before
        % exponent or imaginary unit
else
    result = x(1:end-E); % discard last E digits
end
+5

- , Matlab , Matlab:

, vpa - . toolbox , . .

, . (Un), , Matlab , , "" . , ' (HPF), .

DefaultNumberOfDigits 100 0
DefaultDecimalBase 1

100 0 "" 1. Pi,

pie = hpf('pi',79)
pie = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208

pie = hpf('pi',80)
pie = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089

pie = hpf('pi',81)
pie = 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899

pie = hpf('pi',82)
pie = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998 

. , . - , . . 5 100 , sqrt(5) 100 . , "", .

+4

Source: https://habr.com/ru/post/1695754/


All Articles