From: Kevin A. <al...@se...> - 2004-10-01 19:10:11
|
I was looking at Daryl's source code in his "PythonCard to the Rescue" article and it reminded me about an issue that is probably worth mentioning to a broader audience. His new article is at: http://www.insanitech.com/pythoncard/ The issue can be summarized by the intro to PEP 238. http://www.python.org/peps/pep-0238.html """ The current division (/) operator has an ambiguous meaning for numerical arguments: it returns the floor of the mathematical result of division if the arguments are ints or longs, but it returns a reasonable approximation of the division result if the arguments are floats or complex. This makes expressions expecting float or complex results error-prone when integers are not expected but possible as inputs. """ If you do much drawing code you find yourself mixing floats and integers in calculations and that can lead to unexpected results because of the / operator ambiguity. So you have to force it to do floating point division by doing an explicit conversion of one of the numbers to floating point in the calculation, or instead of using integers like 1, use 1.0 which will also give you a floating point result. Here's an example in the shell: >>> 1 / 2 0 >>> float(1) / 2 0.5 >>> 1.0 / 2 0.5 You can force floating point division today by putting from __future__ import division at the top of your module, notice the change in the result below after doing the import. >>> from __future__ import division >>> 1 / 2 0.5 This may or may not be a big deal for what you are calculating or drawing. In some cases you could find yourself off by a pixel, but sometimes the inaccuracy will be much greater. A simple way to check would be to calculate both ways and print the results. In fact I should probably update many of the PythonCard modules to use "from __future__ import division" since I know there are a lot of places where I convert integer or long arguments to floats. There is an additional wrinkle to this tale. wxPython is nice in that it will automatically convert floating point coordinates to integers or longs before the wxWidgets C++ code actually gets them, so you can use floating point coordinates internally for the greatest accuracy. But you need to be aware that that the conversion truncates the number, 5.9999999 is still 5 when it is drawn, so you need to do an explicit round() if you want the truncated number to to be drawn rounded up. This auto-conversion actually applies to all wxPython arguments where an int or long is expected and thus applies to PythonCard methods wrappers or aliases to wxPython methods. Here is the relevant bit from the wxPython migration guide explaining the details. """ Before Python 2.3 it was possible to pass a floating point object as a parameter to a function that expected an integer, and the PyArg_ParseTuple family of functions would automatically convert to integer by truncating the fractional portion of the number. With Python 2.3 that behavior was deprecated and a deprecation warning is raised when you pass a floating point value, (for example, calling wx.DC.DrawLine with floats for the position and size,) and lots of developers using wxPython had to scramble to change their code to call int() before calling wxPython methods. Recent changes in SWIG have moved the conversion out of PyArg_ParseTuple to custom code that SWIG generates. Since the default conversion fragment was a little too strict and didn't generate a very meaningful exception when it failed, I decided to use a custom fragment instead, and it turned out that it's very easy to allow floats to be converted again just like they used to be. So, in a nutshell, any numeric type that can be converted to an integer is now legal to be passed to SWIG wrapped functions in wxPython for parameters that are expecting an integer. If the object is not already an integer then it will be asked to convert itself to one. A similar conversion fragment is in place for parameters that expect floating point values. """ ka |