# Estimating float32 precision loss

What's the correct way to using Mathematica to get location of true value when using IEEE-754 float32 precision?

Inspired by this question, where the user got a range of different answers when asking for the result of the following computation, I'm curious to see what the error estimates would be for float32.

import math
u = 5.0
for i in range(73):
u = 6*math.cos(u)
print(u)

• An analysis that might be a little bit easier and almost equivalent would be to used fixed point. We know that the output range is always -1 to 1, and we know the number of bits used for the floating point manitssa. Feb 16 at 18:26
• Why do it with 32-bit when 64-bit fails miserably (which it does because of the magnification of round-off error at each of the 73 steps)? Feb 16 at 19:29
• Because you want to know the point at which it fails miserably :) I can get an interval object for 64-bit doing Interval[5.], wondering if there's something similar for 32-bit Feb 16 at 19:55

We can coerce a real number into a 32 bit float in a few ways:

pi = N[\[Pi]];

pi // FullForm

3.141592653589793

ImportString[ExportString[pi, "Real32"], "Real32"][[1]] // FullForm

3.1415927410125732

Normal[NumericArray[{pi}, "Real32"]][[1]] // FullForm

3.1415927410125732


To verify equivalence as a 32 bit float, we see the first 23 digits in the mantissa agree (IEEE 754 standard), other than rounding of the least significant bit:

pi32bit = ImportString[ExportString[pi, "Real32"], "Real32"][[1]];

RealDigits[pi, 2][[1, 2 ;; 24]]

{1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0}

RealDigits[pi32bit, 2][[1, 2 ;; 24]]

{1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1}

• Thanks, this is useful for sanity checking. I found Interval[5.] gives me an interval for float64, was looking for an easy way to get interval for 32-bit Feb 16 at 19:56

Here are a couple of demonstrations that we have no significant digits after 13 or so steps. First we get the base 10 precision for 24 bits mantissas, the common standard for 32 bit floats.

Log[10., 2^24]

(* Out[56]= 7.22472 *)


Now use this to create a bignum 5 with that precision. Then iterate and print precision of the result.

u = 5.7.225;
Do[Print[Precision[u = 6*Cos[u]]], {13}]

(* During evaluation of In[63]:= 5.99705

During evaluation of In[63]:= 4.88645

During evaluation of In[63]:= 4.99221

During evaluation of In[63]:= 4.06717

During evaluation of In[63]:= 3.96362

During evaluation of In[63]:= 3.17044

During evaluation of In[63]:= 2.55444

During evaluation of In[63]:= 1.73966

During evaluation of In[63]:= 2.35372

During evaluation of In[63]:= 2.08599

During evaluation of In[63]:= 1.5386

During evaluation of In[63]:= 0.453482

During evaluation of In[63]:= 0. *)


Alternatively, set up intervals with an epsilon of 2^(-24).

u = Interval[{5. - 2^(-24), 5. + 2^(-24)}];
Do[Print[u = 6*Cos[u]], {13}]

(* During evaluation of In[67]:= Interval[{1.70197,1.70197}]

During evaluation of In[67]:= Interval[{-0.784807,-0.784803}]

During evaluation of In[67]:= Interval[{4.24515,4.24516}]

During evaluation of In[67]:= Interval[{-2.70256,-2.70247}]

During evaluation of In[67]:= Interval[{-5.43098,-5.43074}]

During evaluation of In[67]:= Interval[{3.94887,3.94994}]

During evaluation of In[67]:= Interval[{-4.14881,-4.14418}]

During evaluation of In[67]:= Interval[{-3.22876,-3.20531}]

During evaluation of In[67]:= Interval[{-5.98782,-5.97722}]

During evaluation of In[67]:= Interval[{5.72134,5.74018}]

During evaluation of In[67]:= Interval[{5.07764,5.13697}]

During evaluation of In[67]:= Interval[{2.1431,2.47161}]

During evaluation of In[67]:= Interval[{-4.703,-3.24943}] *)


Same outcome: we no longer have any significant digits at this point.