[PySide] Strange behaviour of QLocale.toString()

Alexey Vihorev vihorev at gmail.com
Mon Apr 15 08:21:26 CEST 2013


First of all - Zak, thank you for your opinion and your efforts to back it
with data - you obviously spend much time and effort on it. The only thing
that I would ask of you is to add a little more brevity into your arguments.
Thanks.

About the matter at hand. When I see something like this.:

 

val = 999999.9

print(PySideLocale().toString(val, 'f', 2)) #999 999,88

print(PyQtLocale().toString(val, 'f', 2)) #999 999,90

print(str(val)) #999999.9

 

.I will inevitably arrive to a conclusion that PySide's behavior is
inconsistent with that of  the language it's written for and of the library
it tries to imitate. You see, maybe you are completely right about rounding,
conversions, math etc and PySide is really giving correct/reasonable
results. But people doing software development need consistency and
predictability. And in this particular case PySide fails to deliver both. 

 

 

From: Zak [mailto:pyside at m.allo.ws] 
Sent: Sunday, April 14, 2013 11:36 PM
To: Alexey Vihorev
Cc: pyside at qt-project.org; thoromyr at mac.com
Subject: Re: [PySide] Strange behaviour of QLocale.toString()

 

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/20130415/3026b29b/attachment.html>


More information about the PySide mailing list