Loren on the Art of MATLAB

Turn ideas into MATLAB

Note

Loren on the Art of MATLAB has been archived and will not be updated.

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.




Published with MATLAB® 7.11


  • print