Double.tostring (again) - how many digits

I know the topic of accuracy of doubles has been discussed many times but my problem is how to get the calculated value printed out - in a textField or a graphics.print event.

It seems that no matter what I use for the formatting (i.e. however many #'s in the format string: “####################0.##”) the output is always limited to 15 or 16 digits before errors start to occur.

Is there a way to print out the number (that is correct in the debugger) onto the screen?

Some simple code:

Dim n As Double = textfield1.Text.ToDouble

Dim s2 As String = n.ToString ( "#.#######################")

g.DrawText(s2, 0, g.FontAscent, g.width)

Dim s3 As String = n.ToString ("############################0.######")

g.DrawText(s3, 0, 20 + g.FontAscent, g.width)

Dim s4 As String = n.ToString ("########################################0.######")

g.DrawText(s4, 0, 40 + g.FontAscent, g.width)

Example outputs:

Screenshot 2023-02-28 at 2.25.41 PM

Whereas the manual says the max value of a double is:

  • The maximum value of a Double is: ±1.79769313486231570814527423731704357e+308

Which looks more like 36 digits plus the exponent.

The number you are trying to output exceeds the resolution of a double which is, if memory serves, 2^53, or around 16 digits.

While a double can hold values that represent a 1.7E±308, only in rounded form after a certain point, hence your output.

See:

I can’t find the resolution of a double on the documentation. I also read the documentation as this is perfect valid:

Dim d As Double = 12345678901234567890.

but d will end with 12345678901234567168.

You are assigning an integer to a double, hence the conversion to a negative value. To ensure that the compiler sees a static value as a double, make sure it ends with “.0”.

A double is 64-bit and is stored according to the standard mentioned above.

1 Like

Thanks, I always forget to put the . at the end.

I updated the code, why the last 3 numbers are 168 instead of 890?

Edit: and if I do this:

Dim s2 As String = d.ToString("#.#")

s2 = “12345678901234567000”

again the last 3 numbers different.

Again, this exceeds the resolution of a 64-bit double which will only be good up to about 16 digits of a whole number. An UInt64 will hold about 19 digits (sometimes 20).

If you need to hold precise values beyond these limits, look to a plugin.

  1. Some higher values don’t have resolution to fit 16 digits.

The same discussion took place yesterday on the SQLite Forum. People insisting on knowing the “meaning” of digits beyond about 16 of them. The answer is that there’s no meaning, and the particular extra digits printed may be an artefact of the printing process.

1 Like

Thank you, is there somewhere in the documentation about this ‘16 digits (or 15) resolution’?

This question was posed by Mr. Anonymous:

Here are some examples to demonstrate the question.

Let’s take a value 1.2345678901234567e+04 (17 significant digits) and get its decimal representation in:

  • C 8-byte double floating-point value (formatted as “%.25e” with gcc printf),
  • C long double floating-point value (formatted as “%.25Le” with gcc printf),
  • as displayed by SQLite printf with “alternate-form-2” format “%!.25e”:

SQLite version 3.41.0 2023-02-21 18:09:37

sqlite> select printf(‘%!.25e’, 1.2345678901234567e+04);
1.2345678901234567092972532e+04

Here are all of the representations together:

value: 1.2345678901234567000000000e+04
C “%.25e”: 1.2345678901234567092615180e+04 (double)
“%!.25e”: 1.2345678901234567092972532e+04 (SQLite)
C “%.25Le”: 1.2345678901234567000244624e+04 (long double)

As one can see, all of the representations retain at least 16 significant figures, as expected in this case for a double.

However, looking beyond the 16 figures, there is some divergence in the displayed values. I understand that these “extra” digits are not random and are due to the IEEE 754 floating-point resolution, in other words, that’s how the real value maps onto available floating-point value. long double offers a higher resolution, so to speak and is the closest to the real value.

SQLite, as I understand, supports only the double. Yet the SQLite’s printed value shows some other version of the “extended” digits. Thus my question about the meaningfulness of these digits. They don’t match either double or long double, though closer to double in this case. Does this extended format mean to match the 'double' representation?

The response from someone else was:

You can “run the conversion” process to any number of output digits your little heart desires. SQLite3, and various other implementations for converting IEEE-754 floating point values to text, have chosen “arbitrary limits” for how long to “loop around the algorithm” which produces output digits.

Whether these “output digits” have any meaning whatsoever is to be determined by the user of the output. Just because you ask for 5.2f output formatting does not mean that the value is between 99.99 and -9.99 and that the rest of the significant digits are zero. Nor does requesting the format .26e mean that the value contains 26 significant digits.

If other words, just because you asked the question:

for i in range(4000):
   digit = int(value / 10)
   print(digit,end='')
   value = (value - digit*10)/10
print()

does not mean that the 4000 digits thus printed have any meaning whatsoever.

An IEEE-754 binary64 floating point number contains about 15.95 decimal digits of accuracy. This means that any 17 digit represenation is sufficient to express the value approximated and to “trip it” though properly functioning input/output routines.

1 Like

It is abundantly documented outside of Xojo. This is not a Xojo issue. If you use a Double, you should be aware of what it means. You’re a programmer. Do your research.

1 Like

So 16 does not conserve all digits, but 15 does.

Hmmm, nope. The remaining digits are just a fractional result of the conversion from base 2 to base 10

As 20/7 is not 2.8 but 2.8571428… There’s a noise fraction after the resolution.

All interesting input thank you.

I suppose it would be useful to have some commentary about 15/16 significant digits in the documentation as the current “Numerical Limits” section in Double states:

  • The maximum value of a Double is: ±1.79769313486231570814527423731704357e+308
  • The minimum value towards zero is: ±4.94065645841246544176568792868221372e-324

…which is rather misleading.

Not necessarily. Could be (I’m no expert) that these are exact values, but that the delta to add/subtract to give the next exact value has, in decimal terms, lots of digits following lots of zeroes.

Limits aren’t precision. You just can’t represent values upper and lower than those.

Thank you Tim. Little harsh, don’t you think?

So I guess there is no note about this in the documentation and some people reading it may think that they can use 123456789012345678912345 like OP.

Yeah, I guess that was a bit harsh. I apologize. It’s just that the concept of a double precision floating point number is such a fundamental concept of computer science, and I forget I’m not talking to people with a CS background.

The question is, how much of computing fundamentals is Xojo obligated to explain?

2 Likes

Nor can you represent those upper or lower limit values - so, regardless of your computing background, it would seem more significant to mention that doubles are limited to about 15 digits of precision.

The actual upper limits would seem to be more in the region of +/-1.79769313486231e+308 since anything greater cannot be printed out.

As for this question, if you want the number that is shown in the debugger, use format instead if tostring. There’s probably a good reason for the difference.