Sunday, October 17, 2010

How Many Digits to Write?

starLoren on the Art of MATLAB
October 14, 2010 3:18 PM
by Loren

How Many Digits to Write?

Recently, my colleague Rob Comer and I were talking about how to write out a number, in decimal, so that if it were read back into MATLAB, would retain its full precision. The question is how many digits to write out. The number depends on several things, including the datatype the value is stored in. In addition, it may depend on the precision of the value - i.e., was it data collected during an experiment in which only two significant figures were recorded? Today I'll post about the solution Rob and I came up with for choosing the number of digits so if you write out the data as a string, you can read it back in to MATLAB with full precision retained.

Contents

Create Some Values

Let's first create some values, both single and double versions of pi.

format long g dblpi = pi snglpi = single(pi)
dblpi =           3.14159265358979 snglpi =          3.141593 

Figure Out Number of Digits

To figure out the number of digits to print, we need to know what the floating point accuracy, sometimes called eps for the number of interest.

eps(snglpi) eps(dblpi)
ans =     2.384186e-007 ans =      4.44089209850063e-016 

As makes sense, we can see that the accuracy of the single precision value is larger than that for the "equivalent" double precision value. That means that the number next closest to the single precision value is farther away than the number next closest to the double precision value.

Number of Digits

We can use eps(x) to help us figure out how many digits to print after the decimal place. First find total number of digits, base 10:

log10(eps(snglpi)) log10(eps(dblpi))
ans =          -6.62266 ans =           -15.352529778863 

To get to a positive number of digits, simply negate the results.

-log10(eps(snglpi)) -log10(eps(dblpi))
ans =           6.62266 ans =            15.352529778863 

And round up to get make sure we don't miss any accuracy.

ceil(-log10(eps(snglpi))) ceil(-log10(eps(dblpi)))
ans =      7 ans =     16 

Let's convert the results to a string. We are taking advantage of the ability to control the number of digits using * in sprintf.

snglpistr = sprintf('%.*f', ceil(-log10(eps(snglpi))), snglpi) dblpistr = sprintf('%.*f', ceil(-log10(eps(dblpi))), dblpi)
snglpistr = 3.1415927 dblpistr = 3.1415926535897931 

Now we've captured each value so if written out as a string, and read back into MATLAB, the accuracy is preserved.

Convert to a Function

Taking what we know for finding the number of digits, let's make a function that we can use to test it out.

digits = @(x) ceil(-log10(eps(x))); printdigs = @(x) sprintf('%.*f', digits(x), x);

Try Some Values

printdigs(pi) printdigs(2/3) printdigs(1000*pi) printdigs(pi/1000)
ans = 3.1415926535897931 ans = 0.6666666666666666 ans = 3141.5926535897929 ans = 0.0031415926535897933 

Some Magic Now

Rob created the necessary magic for getting rid of trailing zeros after the decimal point, while leaving at least one digit to the right of the decimal.

x = 1/2000 str = printdigs(x) strout = stripzeros(str)
x =                     0.0005 str = 0.0005000000000000000 strout = 0.0005 

Let's try some more values. First create a function to help us again.

strippedStringValues = @(x) stripzeros(printdigs(x)); vals = [ 100/289, -1/17, 1/2000, 0, 500 -200, 123.4567] for k = vals     strippedStringValues(k) end
vals =   Columns 1 through 2          0.346020761245675       -0.0588235294117647   Columns 3 through 4                     0.0005                         0   Columns 5 through 6                        500                      -200   Column 7                   123.4567 ans = 0.34602076124567471 ans = -0.058823529411764705 ans = 0.0005 ans = 0.0 ans = 500.0 ans = -200.0 ans = 123.4567 

Here's the magic code for stripping the zeros, for those who are interested.

dbtype stripzeros
1     function str = stripzeros(strin) 2     %STRIPZEROS Strip trailing zeros, leaving one digit right of decimal point. 3     % Remove trailing zeros while leaving at least one digit to the right of 4     % the decimal place. 5      6     %   Copyright 2010 The MathWorks, Inc. 7      8     str = strin; 9     n = regexp(str,'\.0*$'); 10    if ~isempty(n) 11        % There is nothing but zeros to the right of the decimal place; 12        % the value in n is the index of the decimal place itself. 13        % Remove all trailing zeros except for the first one. 14        str(n+2:end) = []; 15    else 16        % There is a non-zero digit to the right of the decimal place. 17        m = regexp(str,'0*$'); 18        if ~isempty(m) 19            % There are trailing zeros, and the value in m is the index of 20            % the first trailing zero. Remove them all. 21            str(m:end) = []; 22        end 23    end  

How Do You Control Printed Digits?

Are you able to just use the default printing from MATLAB for your values? Do you use disp, leave off the semi-colon (;), use one of the *printf functions? What customizations do you need to make to print out values? Let me know here.


Get the MATLAB code (requires JavaScript)

Published with MATLAB® 7.11

Numerical Accuracy Strings


Art Trembanis
CSHEL
Department of Geological Sciences
University of Delaware

No comments: