5

I had written the following function in which values of x,y were passed:

def check(x, y):
    print(type(x))
    print(type(y))
    print(x)
    print(y)
    if x == y:
        print "Yes"

Now when I called check(1.00000000000000001, 1.0000000000000002) it was printing:

<type 'float'>
<type 'float'>
1.0
1.0

Now from the print statements of variables x & y, it was hard for me to debug why x != y (though both were printing same values). Though I resolved it by printing x - y which gave me the difference but is there any way to modify print statement so that to know why x!=y in this particular use case without using any external print libraries and subtraction solution.

bluenote10
  • 16,847
  • 7
  • 76
  • 133
skaul05
  • 1,836
  • 2
  • 13
  • 23
  • I am using Python 2.7 – skaul05 Sep 27 '18 at 07:06
  • This question is not a duplicate of the linked question. The linked question asks for printing with a fixed number of decimals. This question searches for a possibility to get a string representation with **full precision**, but not necessarily a fixed number of decimals. This makes a huge difference, and the solution in the other question (`'{0:.16f}'.format(1.6)`) does not work at all for that purpose, e.g. `1e-300` and `2e-300` have the same fixed decimal string representation and don't satisfy "full precision". – bluenote10 Feb 01 '20 at 09:19
  • I have found a solution to that, but I can't post it anywhere because of wrong duplicate marking... – bluenote10 Feb 01 '20 at 09:29

4 Answers4

4

To get full precision and correct formatting do

format(2**(1/2), '.60g') 
# -> '1.4142135623730951454746218587388284504413604736328125'

and check it with

import decimal
print(decimal.Decimal.from_float(2**(1/2))
# -> '1.4142135623730951454746218587388284504413604736328125'

The g format type switchs to exponential notation when needed.

akuzminykh
  • 3,860
  • 4
  • 10
  • 30
1

What you really need here is decimals. Python float won't allow you for such precision.

In [28]: d= Decimal('1.00000000000000001')

In [29]: print d
1.00000000000000001
1

A simple solution to get a string representation of a float with full precision is to use json.dumps.

JSON serialization/deserialization has to make sure that roundtrips are loss-less, and thus, the implementation produces a string representation you are looking for:

import json

def check(x,y):
    print(json.dumps(x))
    print(json.dumps(y))
    print("x == y is {}".format(x == y))
In [1]: check(1.00000000000000001, 1.0000000000000002)
1.0
1.0000000000000002
x == y is False

In [2]: check(1e-300, 2e-300)
1e-300
2e-300
x == y is False

In [3]: check(1e+300, 2e+300)
1e+300
2e+300
x == y is False

This also clarifies that 1.00000000000000001 actually is 1.0. This can also be checked by enumerating the numbers around 1.0 using np.nextafter, which produces the next larger/smaller representable floating point value:

    0.9999999999999994
    0.9999999999999996
    0.9999999999999997
    0.9999999999999998
    0.9999999999999999
[*] 1.0               
    1.0000000000000002
    1.0000000000000004
    1.0000000000000007
    1.0000000000000009
    1.000000000000001 

Another note: json.dumps has a feature that in some cases can be annoying: It even supports NaN and +/- infinite values even though they are not part of the JSON standard, which is actually beneficial in this use case.


An alternative I've seen is to use "{:.17g}".format(x) based on the g format specifier. The formatter also switches from e notation to fixed notation when necessary, and 17 digits should always be sufficient precision, but I haven't verified that. It can produce too many digits in some cases though.

bluenote10
  • 16,847
  • 7
  • 76
  • 133
-1

a solution to a similar case was discussed here: print float to n decimal places including trailing 0's

I don't know though if there is a way of printing the full length of the float. But I guess if you use like 100 values after a floating point, it will solve must of your problems.

akhavro
  • 101
  • 7
  • 1
    In that case, I have to mention maximum decimal places beforehand which can fail in certain cases. – skaul05 Sep 27 '18 at 07:09
  • String formatting doesnt help here: print '{:.20f}'.format(1.00000000000000001) -> 1.00000000000000000000 – Radosław Ganczarek Sep 27 '18 at 07:13
  • @RadosławGanczarek That's because this value is indeed exactly one. But notice that the `1.0000000000000002` in the OPs example has one less zero digit, and `'{:.20f}'.format(1.0000000000000002)` returns `1.00000000000000022204`. – bluenote10 Feb 01 '20 at 09:11