3

I want to print floating point numbers which contain variable precision.

I have multiple numbers such as:

0.634564644534135499
0.0005462007746487777
0.028820785252590582
0.0018751147995774936
0.0075146048125540816
0.00046670455

I want to get the same numbers as output using print. I know that the number of decimal places can be fixed using print("{:.19f}".format(0.0005462007746487777)) but I don't want to make the number of decimal places fixed. Since different numbers will have different decimal places

Code

#!/usr/bin/env python3
 
number_1=0.634564644534135499
number_2=0.0005462007746487777
number_3=0.028820785252590582
number_4=0.0018751147995774936
number_5=0.0075146048125540816
number_6=0.00046670455

print("Number 1: ",number_1)
print("Number 2: ",number_2)
print("Number 3: ",number_3)
print("Number 4: ",number_4)
print("Number 5: ",number_5)
print("Number 6: ",number_6)

Actual Output:

Number 1:  0.6345646445341355
Number 2:  0.0005462007746487777
Number 3:  0.028820785252590582
Number 4:  0.0018751147995774936
Number 5:  0.0075146048125540816
Number 6:  0.00046670455

Required Output:

Number 1: 0.634564644534135499
Number 2: 0.0005462007746487777
Number 3: 0.028820785252590582
Number 4: 0.0018751147995774936
Number 5: 0.0075146048125540816
Number 6: 0.00046670455

What I don't understand is why Number 2(which has higher precision) is being printed correctly but Number 1 losses its precision?

5
  • This comes down to the algorithm used by python for float decimal string representation. It uses David Gay’s algorithm for finding the shortest floating point representation that doesn’t change its value. There is no way to get back the "number you wrote in" because many possible decimal literals will map to the actual float representation. Commented May 3, 2021 at 9:03
  • Do you understand, fundamentally, that many decimal numbers that you input as floating point literals are not able to be represented at all? This comes down to the machine representation being binary, and the fixed-size nature of float objects, which are basically wrappers around C double's. On top of this is the issue of how a float object is printed. Commented May 3, 2021 at 9:05
  • 1
    Why do you need the numbers in exactly the same way? Commented May 3, 2021 at 9:36
  • @Daniel I need them for testing purposes. I have 2 applications. One in C++ and one python (which is a reference application used to test different functionality of C++). Both applications receive these numbers in a json message and both have to print each field of the json message. The C++ prints the number in the correct way whereas python has this limitation Commented May 3, 2021 at 10:15
  • 2
    @Saad_Khan: there is not one correct way. Python is more correct, in the sense, that it is guarenteed that it uses the fewest decimal places. Different C++-Implementations can use different methods and lead to different results. When comparing results, you have to handle the different representations of the same number. Commented May 3, 2021 at 10:39

2 Answers 2

3

Fundamentally what you require is not possible with float objects, which are basically wrappers around C-doubles, so 64-bit floating point numbers.

There are several things going on here. There is the fundamental problem with mapping a decimal literal input in your source code:

0.634564644534135499

To an actual machine representation, which is not decimal, but binary.

Due to the inherent constraints of the 64-bit floating point format, multiple decimal inputs will be mapped to the same underlying representation:

>>> 0.634564644534135499 == 0.6345646445341355
True

Which is actually neither of those, the exact decimal is actually:

>>> import decimal
>>> decimal.Decimal(0.6345646445341355)
Decimal('0.6345646445341355246227976749651134014129638671875')
>>> decimal.Decimal(0.634564644534135499)
Decimal('0.6345646445341355246227976749651134014129638671875')

What is actually printed when you naively print(some_float) is going to be a representation which is guaranteed to map back to the same float that produced it if you enter it as a float decimal literal. In fact, since Python 3.1 CPython uses David Gray's algorithm for finding the shortest such representation that preserves the value.

So, as is often the case when encountering troubles due to the inherent limitation of float, try using decimal.Decimal. Remember, the input should be a string. The following script:

import decimal
number_1 = decimal.Decimal('0.634564644534135499')
number_2 = decimal.Decimal('0.0005462007746487777')
number_3 = decimal.Decimal('0.028820785252590582')
number_4 = decimal.Decimal('0.0018751147995774936')
number_5 = decimal.Decimal('0.0075146048125540816')
number_6 = decimal.Decimal('0.00046670455')

print("Number 1: ",number_1)
print("Number 2: ",number_2)
print("Number 3: ",number_3)
print("Number 4: ",number_4)
print("Number 5: ",number_5)
print("Number 6: ",number_6)

Prints:

Number 1:  0.634564644534135499
Number 2:  0.0005462007746487777
Number 3:  0.028820785252590582
Number 4:  0.0018751147995774936
Number 5:  0.0075146048125540816
Number 6:  0.00046670455
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for the detailed explanation. I don't exactly have control over the input. I am getting data from somewhere else in float point format and I need to print it as is. If there is any other method you might be familiar with which I can use to print the actual value, kindly let me know
@Saad_Khan what do you mean by the actual value?
@Saad_Khan I would strongly recommend reading through this: docs.python.org/3/tutorial/floatingpoint.html alongside the already great answers here. What you are seeing is the best your machine can do
@juanpa.arrivillaga By actual value I mean I want to print 0.634564644534135499 instead of 0.6345646445341355.
@Saad_Khan based on what? You need to read what I wrote carefully.
1

So here's a reference image of a number in general (taken off of the web from this link https://www.log2base2.com/number-system/float-to-binary-conversion.html): Any floating point number

Python uses each bit to store the integral part and the fractional part and uses 64 bits to store a floating point value. So if the integral part(absolute value) is high, it then it automatically means that the fractional part will have lesser bits for storage.

In the examples provided by you, since each value has integral part of 0, then the comparison can be done entirely on the fractional part.

So for this case, we will just look at the values that are in the fractional parts.

In the first example (0.634564644534135499) the number of values after the decimal point although are lesser than in the second case (0.0005462007746487777), but the number of bits required are more in the first case than in the second case. So the value stored will be rounded off to ensure that the number of bits are as per the requirement in python.

So to change this, one way that I can think of is to store the values within quotes in the form of a string. It will work, but assuming that you'll be getting the data from elsewhere, not sure if this is very good:

number_1="0.634564644534135499"
# number_3=0.028820785252590582
# number_4=0.0018751147995774936
# number_5=0.0075146048125540816
# number_6=0.00046670455

print("Number 1: ",number_1)
# print("Number 2: ",number_2)

Another way is to use decimal.Decimal('0.634564644534135499'). In case I haven't answered your doubt, as to why does it happen that way let me know please, I'll try and explain better.

2 Comments

Thanks for the explanation. I am getting data from elsewhere in float point format so I can not store it within quotes and converting to string will also cause it to lose the actual number. Is there any other method you are familiar with which I can use to print the actual number
Hi really Sorry for the late response. But it's not so straight forward. You might have to make changes in every place. Since let's say your data travels through multiple stops, even if there's one place where it's float, it will round off the value to 64 bits. Are you having control over the data source ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.