[PySide] Strange behaviour of QLocale.toString()

Zak pyside at m.allo.ws
Sun Apr 14 22:35:40 CEST 2013


My original email was correct, let me provide proof and explain further.

The proof is attached as a Python script, you can run it yourself. I ran 
it on Mac OS X and also on Windows 7, and I ran it with both 32-bit and 
64-bit versions of PySide and PyQt (and matching 32- or 64-bit versions 
of the Python interpreter). The results were as follows:

1. PySide uses 32-bit floating points. It does not matter whether PySide 
was compiled in 32-bit or 64-bit mode (x86 or x64).

2. PyQt uses 64-bit floating points. It does not matter whether PyQt was 
compiled in 32-bit or 64-bit mode.

PyQt has better precision than PySide, because PyQt uses 64-bit floats 
(just like Python itself). However, both PyQt and PySide are internally 
representing the numbers as binary floating point numbers.

Let me address a few specific points:

Tim Doty wrote:

 > For the PySide what is really happening is not a rounding
 > error/precision as suggested, but printing an integer with floating
 > point notation. When the floating point is converted to an integer
 > it is rounded. To satisfy yourself that this is in fact happening
 > change the number to be printed (e.g., 9999999.5 vs 9999999.4).

This is incorrect, what is happening is a rounding error. The number is 
being rounded to fit in a 32 bit piece of memory. The counter-example 
above is misleading because it uses bad values. Try the values 1.4 and 
1.6 instead. These values are used in the attached Python script.

The number is not at any point represented as an integer (data 
structure), it is always a floating point. Even if the number's value is 
a whole number, it is represented as a floating point. For instance, 
Python interprets '1.0' as a floating point literal. A mathematician 
would say 1.0 is an integer, but computer science uses different 
terminology. In computer science, in the language Python, '1' is an 
integer and '1.0' is a floating point, and they are equal in numerical 
value. They are represented differently in memory.

Alexey Vihorev wrote:

 > Two considerations:
 > 1. No rounding should take place in this case - the decimal precision 
of the
 > input and output is exactly the same.
 > 2. In PyQt it *does* work correctly, so fundamental lows of computer 
science
 > are certainly not the reason.

In response to 1: That is just your opinion. You do not want rounding to 
take place. The designers of Python and Qt had a different opinion. To 
make things faster, they decided to round numbers so that they can fit 
in either 32 or 64 bits of memory. If you want to use larger chunks of 
memory and keep higher precision, then you should use the Python 
standard library 'decimal', as I said before. Using decimal.Decimal 
objects, you can set the precision yourself. Because you are not using 
this library, Python, PySide, and PyQt are making a decision about how 
to round the number. PySide chose 32 bits, PyQt chose 64 bits. The 
Python language itself chose 64 bits, so PySide and PyQt cannot possibly 
choose higher precision, the extra bits have already been thrown away.

In response to 2: You are using incorrect logic. As you can see from my 
example, PyQt also suffers rounding errors, they just occur around 15 to 
17 digits. You didn't use that many digits so you didn't see the 
rounding error.

Zak Fallows

# File: num_print.py

from PyQt4.QtCore import QLocale as PyQtLocale
from PySide.QtCore import QLocale as PySideLocale

def pb(num):
     """pb stands for Print Both"""

     print "Original: " + num
     flt = eval(num)
     print "    PyQt: ",
     print PyQtLocale().toString(flt, 'f', 6)
     print "    PySide: ",
     print PySideLocale().toString(flt, 'f', 6)
     print ''

test_list = [
     '1.4',
     '1.6',
     '1000.4',
     '1000.6',
     '10**6 + .4',
     '10**6 + .6',
     '10**7 + .4',
     '10**7 + .6',
     '10**8 + .4',
     '10**8 + .6',
     '10**9 + .4',
     '10**9 + .6',
     '10**14 + .4',
     '10**14 + .6',
     '10**15 + .4',
     '10**15 + .6',
     '10**16 + .4',
     '10**16 + .6',
     '10**17 + .4',
     '10**17 + .6',
]

for num in test_list:
     pb(num)

"""

#======================== How to Check Version Numbers 
========================#

PyQt:

     PyQt4.QtCore.PYQT_VERSION_STR

PySide:

     PySide.__version__

#=============================== Sample Output 
================================#

Here is some sample output from running the program:

#---------------------------------- Mac OS X 
----------------------------------#

Mac OS X 10.8.3
Python 2.7.3, 64-bit, from Python.org
PySide 1.1.1, 64-bit
PyQt 4.9.4, 64-bit

 >>> import num_print
Original: 1.4
     PyQt:  1.400000
     PySide:  1.400000

Original: 1.6
     PyQt:  1.600000
     PySide:  1.600000

Original: 1000.4
     PyQt:  1,000.400000
     PySide:  1,000.400024

Original: 1000.6
     PyQt:  1,000.600000
     PySide:  1,000.599976

Original: 10**6 + .4
     PyQt:  1,000,000.400000
     PySide:  1,000,000.375000

Original: 10**6 + .6
     PyQt:  1,000,000.600000
     PySide:  1,000,000.625000

Original: 10**7 + .4
     PyQt:  10,000,000.400000
     PySide:  10,000,000.000000

Original: 10**7 + .6
     PyQt:  10,000,000.600000
     PySide:  10,000,001.000000

Original: 10**8 + .4
     PyQt:  100,000,000.400000
     PySide:  100,000,000.000000

Original: 10**8 + .6
     PyQt:  100,000,000.600000
     PySide:  100,000,000.000000

Original: 10**9 + .4
     PyQt:  1,000,000,000.400000
     PySide:  1,000,000,000.000000

Original: 10**9 + .6
     PyQt:  1,000,000,000.600000
     PySide:  1,000,000,000.000000

Original: 10**14 + .4
     PyQt:  100,000,000,000,000.406250
     PySide:  100,000,000,376,832.000000

Original: 10**14 + .6
     PyQt:  100,000,000,000,000.593750
     PySide:  100,000,000,376,832.000000

Original: 10**15 + .4
     PyQt:  1,000,000,000,000,000.375000
     PySide:  999,999,986,991,104.000000

Original: 10**15 + .6
     PyQt:  1,000,000,000,000,000.625000
     PySide:  999,999,986,991,104.000000

Original: 10**16 + .4
     PyQt:  10,000,000,000,000,000.000000
     PySide:  10,000,000,272,564,224.000000

Original: 10**16 + .6
     PyQt:  10,000,000,000,000,000.000000
     PySide:  10,000,000,272,564,224.000000

Original: 10**17 + .4
     PyQt:  100,000,000,000,000,000.000000
     PySide:  99,999,998,430,674,944.000000

Original: 10**17 + .6
     PyQt:  100,000,000,000,000,000.000000
     PySide:  99,999,998,430,674,944.000000

#--------------------------------- Windows 7 
----------------------------------#

Windows 7, 64-bit
Python 2.7.3, 32-bit, from Python.org
PySide 1.1.1, 32-bit
PyQt 4.10, 32-bit

 >>> import num_print
Original: 1.4
     PyQt:  1.400000
     PySide:  1.400000

Original: 1.6
     PyQt:  1.600000
     PySide:  1.600000

Original: 1000.4
     PyQt:  1,000.400000
     PySide:  1,000.400024

Original: 1000.6
     PyQt:  1,000.600000
     PySide:  1,000.599976

Original: 10**6 + .4
     PyQt:  1,000,000.400000
     PySide:  1,000,000.375000

Original: 10**6 + .6
     PyQt:  1,000,000.600000
     PySide:  1,000,000.625000

Original: 10**7 + .4
     PyQt:  10,000,000.400000
     PySide:  10,000,000.000000

Original: 10**7 + .6
     PyQt:  10,000,000.600000
     PySide:  10,000,001.000000

Original: 10**8 + .4
     PyQt:  100,000,000.400000
     PySide:  100,000,000.000000

Original: 10**8 + .6
     PyQt:  100,000,000.600000
     PySide:  100,000,000.000000

Original: 10**9 + .4
     PyQt:  1,000,000,000.400000
     PySide:  1,000,000,000.000000

Original: 10**9 + .6
     PyQt:  1,000,000,000.600000
     PySide:  1,000,000,000.000000

Original: 10**14 + .4
     PyQt:  100,000,000,000,000.406250
     PySide:  100,000,000,376,832.000000

Original: 10**14 + .6
     PyQt:  100,000,000,000,000.593750
     PySide:  100,000,000,376,832.000000

Original: 10**15 + .4
     PyQt:  1,000,000,000,000,000.375000
     PySide:  999,999,986,991,104.000000

Original: 10**15 + .6
     PyQt:  1,000,000,000,000,000.625000
     PySide:  999,999,986,991,104.000000

Original: 10**16 + .4
     PyQt:  10,000,000,000,000,000.000000
     PySide:  10,000,000,272,564,224.000000

Original: 10**16 + .6
     PyQt:  10,000,000,000,000,000.000000
     PySide:  10,000,000,272,564,224.000000

Original: 10**17 + .4
     PyQt:  100,000,000,000,000,000.000000
     PySide:  99,999,998,430,674,944.000000

Original: 10**17 + .6
     PyQt:  100,000,000,000,000,000.000000
     PySide:  99,999,998,430,674,944.000000

"""

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/pyside/attachments/20130414/2f05f52f/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: num_print.py
Type: text/x-python-script
Size: 4991 bytes
Desc: not available
URL: <http://lists.qt-project.org/pipermail/pyside/attachments/20130414/2f05f52f/attachment.bin>


More information about the PySide mailing list