12

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.

wjandrea
  • 28,235
  • 9
  • 60
  • 81
skaul05
  • 2,154
  • 3
  • 16
  • 26
  • I am using Python 2.7 – skaul05 Sep 27 '18 at 07:06
  • 1
    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
  • Does `print(repr(x))` not do the job? – ɲeuroburɳ Jul 24 '21 at 19:50

4 Answers4

8

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
  • 4,522
  • 4
  • 15
  • 36
angeldeluz777
  • 319
  • 3
  • 4
  • 1
    This answer produces a string representation that uses much more digits than the IEEE standard requires (wasting space by more than a factor of 2). See my answer for a string representation that produces the **minimal** literal to represent a float. – bluenote10 Aug 05 '21 at 17:51
6

UPDATE: In Python 3 str on a float is guaranteed to produce a string literal with full precision

This was not the case in Python 2. For instance str(1.0000000000000002) was '1.0' in Python 2, but in Python 3 it gives '1.0000000000000002' as expected.

This has often been a source of confusion because a simply print(x) may not have printed x with the full precision, making wrong conclusions -- like in the example of the question.

For the background of this change see here.


Python 2 solution

A simple solution to get a string representation of a float with full precision is to use either repr or 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:

def check(x, y):
    print(repr(x))
    print(repr(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 

In reply to @Robert:

json.dumps also works in your case. The formatting with '.60g' simply produces a literal that has more unnecessary digits than a IEEE double precision number can hold. The precision of the other literal produced by json.dumps is sufficient to represent that particular floating point number, which you can check by:

enter image description here

The two IEEE literals closest to 1./math.sqrt(3.) are:

enter image description here

The first one the closest possible representation already, storing further digits of 1./math.sqrt(3.) will always give you back that same number.

json.dumps has loss-less round-trips for 64-bit IEEE floating point numbers, so it is guaranteed that the number of digits it produces is sufficient.

bluenote10
  • 23,414
  • 14
  • 122
  • 178
  • json.dumps actually does not work for me. ```json.dumps(1./math.sqrt(3.))``` prints 0.5773502691896258 where ```format(1./math.sqrt(3.), '.60g')``` prints 0.57735026918962584208117050366126932203769683837890625. – Robert Andreas Fritsch Aug 05 '21 at 12:43
  • 1
    @RobertAndreasFritsch The precision of the `json.dumps` literal is sufficient in this case as well. I've added a bit of explanation in the answer. Hope it helps. – bluenote10 Aug 05 '21 at 17:37
  • you are right. I was fooled by the number of digits. – Robert Andreas Fritsch Aug 20 '21 at 23:36
  • 1
    `str(0.1)` doesn't seem to print full precision on python 3.10.5 – Felix Bertoni Sep 06 '22 at 15:28
  • @FelixBertoni `"0.1"` is already "full" precision in the sense of sufficient precision. Check against e.g. `float(f"{0.1:.30f}") == 0.1` or experiment with `np.nextafter(0.1, np.inf)` and `np.nextafter(0.1, -np.inf)`. – bluenote10 Sep 06 '22 at 16:19
  • 1
    @bluenote10 Oh, my bad ! I was confusing full precision with exact value --' Maybe you could edit your post to make it clear to others ? This would be nice ! – Felix Bertoni Sep 07 '22 at 18:05
  • @bluenote10 I am using Python 3 but print is truncating 32 b decimals to display only 8 decimals? Why, or what can I do? Thanks if you can help. – EGME Oct 26 '22 at 08:04
  • @EGME It may be best to raise that as a separate question, because you are really referring to the `decimal` type whereas this question is talking about floating point numbers, which is quite different. – bluenote10 Oct 27 '22 at 06:10
  • @FelixBertoni What is the difference? – Joseph Garvin Dec 03 '22 at 17:23
  • 1
    @JosephGarvin When storing numbers in FP, there is some kind of rounding occurring. So 0.1 in FP isn't exactly 0.1 in memory, but an approached value, say X. If we print X as 0.1 in "full precision", it means that X is the FP representaiton of 0.1. If we convert 0.1 back, we fall back on X stored in FP. On the other hand, "exact value" means actually printing X, the exact value stored, and not 0.1, one of the other values mapped to it. – Felix Bertoni Dec 04 '22 at 15:26
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
Smooth Fire
  • 183
  • 5
-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 – Smooth Fire 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