2

Is the following true for all float numbers r from 0 to 1 in CPython 3.10 or newer that uses IEEE-754 floating-point numbers?

float('%.17g' % r) == r

In other words, is 17-digit decimal representation of float numbers from 0 to 1 accurate enough to be equal to their binary counterparts?

My conjecture is that it is true for 17 digits (but not 16), as I did not find a counterexample yet with the following program:

import random

def check(precision=17):
  r = random.random()
  d = float(f'%.{precision}g' % r) - r
  if d != 0:
    print(d)

for i in range(1000000000):
  check(17)

However, this does not cover all cases, so I am searching for an answer grounded in theory.

Depending on the answer, I am searching for the answer to the following quesitons:

  • Would it be true for all float numbers?
  • If the conjecture is not true for 17 digits, is it true for more?
  • If the conjecture is not true, is at least each float('%.17f' % r) unique?
Xilexio
  • 1,178
  • 19
  • 40
  • No, because it's impossible to represent every binary value as a decimal number. – martineau Jun 26 '22 at 15:25
  • It is not really relevant to the question, but I am asking this because I name my files that represent a state of a certain system at given point in time by a 17-digit precision filenames and I want to confirm if this approach requires me to store the precise time elsewhere and also if it can load to a loss of data (if the transformation is not unique). – Xilexio Jun 26 '22 at 15:27
  • @martineau I am not asking whether all binary numbers can be represented in a decimal system finitely. I am asking about a specific set of (I think 2^53) binary numbers representable by IEEE-754 floats and whether their conversion to 17-digit decimals and back (with rounding included) is exact. Specifically, I am certain there exists a finite number of digits for which this is true as 2^-53 > 10^-16. It does not work for 16 digits, but I am pretty sure it will for either 17 or 18, since rounding can change only so much. – Xilexio Jun 26 '22 at 15:32
  • 2
    Yes, on _most_ IEEE 754-using systems and recent-ish CPython, `float("%.17g" % x) == x` will give `True` for any non-NaN float `x` (not just floats in the range `(0, 1)`). That's a consequence of CPython using David Gay's `dtoa.c`, which provides correctly rounded str<->float conversions. However, there may be rare systems that can't use `dtoa.c`, and on those systems the system's str<-> float conversions are used, and the equality you're asking about can't be guaranteed. – Mark Dickinson Jun 26 '22 at 15:32
  • 2
    And yes, 17 is the right number of significant digits to use for IEEE 754 binary64; documented in lots of places. See for example https://www.exploringbinary.com/17-digits-gets-you-there-once-youve-found-your-way/ – Mark Dickinson Jun 26 '22 at 15:34
  • @MarkDickinson Thank you. If you can put these two comments into an answer, I will accept it. – Xilexio Jun 26 '22 at 15:35
  • @Xilexio: I don't have time right now for a proper answer, I'm afraid; I may find that time later. (Happy if someone else beats me to it, though.) – Mark Dickinson Jun 26 '22 at 15:36
  • 3
    @martineau It's irrelevant to this particular question (which only asks whether they can round-trip correctly), but your statement is actually wrong, every finite binary value has a finite decimal value. – user202729 Jun 26 '22 at 16:13
  • Side note, a `repr` round-trips as well and gives "nicer" result when possible. https://stackoverflow.com/questions/39618943/why-does-the-floating-point-value-of-40-1-look-nice-in-python-3-but-30-1-doesn – user202729 Jun 26 '22 at 16:19
  • @user202729: My claim was you can't represent that decimal value (not that it didn't exist). – martineau Jun 26 '22 at 16:19
  • 4
    @martineau: maybe you intended to claim that not every decimal number has a binary representation, but what your comment actually says is that not every binary number has a decimal representation, and that's not correct. – rici Jun 26 '22 at 16:56
  • 3
    If your actual problem is to store binary floating point numbers exactly, why not save them as hex-floats? Or directly as binary? – chtz Jun 26 '22 at 22:00
  • @chtz Good point. This is because i never heard of hex-floats until Mark Dickinson linked me to that article. However, I'd still prefer to use decimals in my case as the strings in question are filenames that I will be operating on using console, with intention of doing some operations for given time or time range range (known in decimal, sometimes approximately). – Xilexio Jun 27 '22 at 15:33

0 Answers0