[PySide] Strange behaviour of QLocale.toString()

Tim Doty thoromyr at mac.com
Sun Apr 14 23:04:15 CEST 2013


On Apr 14, 2013, at 3:35 PM, Zak <pyside at m.allo.ws> wrote:

> My original email was correct, let me provide proof and explain further.
> 
> 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.

As I said, I don't know the PySide internals. I referred to the published documentation which implies that floats are being coerced into integers due to lack of a float overload. Apparently the docs are wrong… go figure.

Interestingly, if you drop the millions (e.g., 999999.62) you can see the rounding in action. I'm not going to dredge through a wall of text looking for a point in the preceding post, but -- as it so happens -- the example I give above outputs correctly making it seem as if there is inconsistent precision. But it is an accident. 999999.65 also prints as 999999.62 while 999999.66 prints as 999999.69

So it is just an artifact of 32-bit precision float representations. Now, why it is being constrained to a 32-bit float on a 64-bit architecture is a different conversation and, while I recognize there would be disagreement, I call that a bug on its face and the fact that the sibling implementation uses 64-bit precision underscores the point.

Tim Doty

> 
> 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
> 
> """
> 
> <num_print.py>




More information about the PySide mailing list