9
$\begingroup$

Those who visit the chat might have seen the question of varkor. I'm posting it here in the hope that I have missed something.

Assume you have a real number

real = 57.295780181884765625;

This number fits exactly into a 32 bit floating point number (known as IEEE-754) as you can verify on this site. However, exporting and importing it again looses information:

ImportString[ExportString[real, "Real32"], "Real32"] // InputForm
(* {57.295780181884766} *)

Additionally, you can check that the precision of real is about 20

Precision[real]
(* 19.7581 *)

while the re-imported number only has MachinePrecision which is about 15. Actually, the situation is worse because even if you use "Real64", the re-imported number will still have only MachinePrecision and the loss of information is exactly as large as when using "Real32".

I was curious if the exported bytes are wrong or if the the re-import fails. We can store the binary value and re-import it as "Binary" so that we get the actual bytes. These bytes are then turned into a bit pattern (the Reverse is due to the byte ordering):

bin = ExportString[real, "Real32"];
digits = Flatten[IntegerDigits[Reverse@ImportString[bin, "Binary"], 2, 8]]
(* {0,1,0,0,0,0,1,0,0,1,1,0,0,1,0,1,0,0,1,0,1,1,1,0,1,1,1,0,0,0,0,1} *)

The bits are exactly as given on the site I linked above and we can turn them back into a number by following the calculation of IEEE-754

e = Sum[digits[[9 - i]]*2^i, {i, 0, 7}];
fraction = 1 + Sum[digits[[9 + i]]*2^(-i), {i, 1, 23}];
result = 2^(e - 127)*fraction;
N[result, 30]
(* 57.2957801818847656250000000000 *)

As you can see, the result is exactly what we have used as input.

Question: Why is a "Real32" or a "Real64" not imported correctly? You will find that using "Real128" indeed returns the correct result and a number with an explicit precision. What is happening here?

$\endgroup$
2
  • 3
    $\begingroup$ The basic reason is that there are only two types of real numbers in Mathematica: machine precision reals (IEEE-754 doubles), and arbitrary precision reals. If the kernel could work with native IEEE-754 floats, their precision would of course be half of MachinePrecision. The ~20 or so precision that you are seeing is an artifact of representing the number as a bigreal. When importing, anything that fits into a native double will be converted to one. "Real128" of course, can only return a bigreal. $\endgroup$
    – ilian
    Commented Mar 3, 2018 at 4:53
  • 2
    $\begingroup$ Well, not exactly half, but a bit less, 24/53. Also, the point of making everything that fits into a native double is so that one could have a packed array of them. $\endgroup$
    – ilian
    Commented Mar 3, 2018 at 5:14

1 Answer 1

7
$\begingroup$

I don't think there's a problem. It's a question of output-formatting, connected with the fact that there are single-precision binary fractions that cannot be represented in decimal form in 17 digits or less.

The OP notes that the input real has more than machine precision. Thus it is typeset with more than $MachinePrecision digits. ($MachinePrecision is approximately 15.95). However, the imported "Real32" number has machine precision, which, as the OP implies, should be sufficient for a 32-bit real, since machine precision is double precision (binary64) and a "Real32" is single precision (binary32):

in = First@ImportString[ExportString[real, "Real32"], "Real32"];
Precision[in]
(*  MachinePrecision  *)

The decimal output for in is rounded to 17 digits in InputForm so some digits appear to be lost. The floating-point real is exactly equal to

exact = SetPrecision[real, Infinity]
(*  15019745/262144  *)

Converted to a machine precision number, the value of real is typeset in decimal InputForm as

N@real // InputForm
(*  57.295780181884766  *)

This is the same as the imported "Real32" value. And if we examine the exact fraction for the imported in, we see it is the same as real:

SetPrecision[in, Infinity]
(*  15019745/262144  *)

It is possible that one might think that since real has 19+ digits of precision, outputting it in "Real32" form should also maintain that precision. But I think that "Real32" represents single-precision. If it were to have an arbitrary precision, the precision should be Log10[2^24] or about 7.22.

$\endgroup$
4
  • $\begingroup$ +1, but it is not just a matter of output-formatting or typesetting. One number is a bigreal, and the other is a machine real, so they have a completely different internal representation. $\endgroup$
    – ilian
    Commented Mar 3, 2018 at 5:19
  • $\begingroup$ @ilian Thanks. I realize the internal difference. I'm suggesting that the OP's conclusion that there is a problem is based on the formatting of bigreals vs machine reals. Or that's what I intended. $\endgroup$
    – Michael E2
    Commented Mar 3, 2018 at 5:27
  • 2
    $\begingroup$ I see now that the binary representation of the imported number is the same. So what got lost in the export/import cycle is the information of the precision which also determines how many decimal digits are shown by Mathematica. $\endgroup$
    – halirutan
    Commented Mar 3, 2018 at 5:39
  • $\begingroup$ Thanks again for your answer and of course I highly appreciate the comments of @ilian. $\endgroup$
    – halirutan
    Commented Mar 6, 2018 at 12:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.