• Subject: Re: Formatting numbers without precision loss
• From: Lars Müller <appgurulars@...>
• Date: Tue, 20 Jun 2023 14:03:49 +0200

Exponential binary notation is indeed the only way to exactly represent floats. If you're in full control of the format, it's the way to go for serializing floats; it's effectively just the exponent plus a hexdump of the mantissa. This also makes it very efficient and easy to implement. You could even implement this formatting yourself in Lua.

But if your hands are tied with JSON or similar formats - or you want human-readability - you need to format as decimal. This can't be exact, but it can be precise enough for the conversion from string back to number to yield the exact same number.

For this, 17 significant digits should suffice:

"The 53-bit significand precision gives from 15 to 17 significant decimal digits precision" - https://en.wikipedia.org/wiki/Double-precision_floating-point_format

Thus ("%.17g"):format(num) should work. If you really want to be safe, perhaps use %.18g.

Note: Do not use %f, use %g instead. %f does not count significant digits, it counts digits after the decimal dot. It's also very human-unfriendly and wasteful since it will emit trailing zeroes; the fact that it doesn't use exponents can create very bloaty outputs for numbers with large exponents. Here's ("%.6f"):format(2^1e3):

10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376.000000

On 20.06.23 13:49, Lorenzo Donati wrote:
Hi!

[...]

Just a hunch, since it passed too much time from when I looked into this. I think the only fireproof way to serialize a float number (assuming an IEEE754 implementation) is using exponential binary notation ("%a" format specifier), but the presence of that depends on the underlying C runtime (IIRC Lua supported that format specifier since v5.2).

In other words, there always will be some float number whose binary representation will be approximate by any amount of decimal figures in a decimal representation.

There was some "good enough" approximation: IIRC %.14f worked well for 64bit double precision IEEE754 floats. "Well" means that for most (all?) possible numbers the approximation was under some epsilon.

Cheers!

-- Lorenzo