[Python-Dev] RE: FixedPoint and % specifiers.

Tim Peters tim.one@comcast.net
Wed, 05 Feb 2003 20:12:20 -0500


There are no ways for classes to hook into % formats, short of that if you
use %d, __int__ will be called; if you use a float format code, __float__
will be called; if you use %s, __str__ will be called.

[David]
> Displaying FixedPoints:
>
> Python 2.2.1 (#34, Jul 16 2002, 16:25:42) [MSC 32 bit (Intel)] on win32
> Type "help", "copyright", "credits" or "license" for more information.
> Alternative ReadLine 1.5 -- Copyright 2001, Chris Gonnerman
> >>> from fixedpoint import *
> >>> a = FixedPoint(2.135)

Note that the docs strongly advise against initializing a FixedPoint from a
float.  Until a decimal literal notation is added to the core (if ever),
you're much better off passing a string literal.  The docs contain examples
of why.

> >>> print '%d' % (a)
> 2
> >>> print '%4d' % (a)
>    2
> >>> print '%e' % (a)
> 2.130000e+000
> >>> print '%f' % (a)
> 2.130000
> >>> print '%2f' % (a)
> 2.130000
> >>> print '%.2d' % (a)
> 02
> >>> print '%s' % (a)
> 2.13
> >>> a.set_precision(4)
> >>> print '%s' % (a)
> 2.1300
>
> I naively expected the %d to produce 2.315

Why?  %d is an integer format code, so of course it produces an integer.
You can't start reading, e.g., "%d" as "decimal" just because you imported a
module <wink>.

> - and it would be nice, after the manner of C, to be able to specify the
>   precision (or scale if you prefer) of the output as I attempted in
>   '%.2d' and also (not shown) '%2.2d'.  This might need some work since
>   %<num>d is a field filler spec. It would be nice if given 2.135:
> 	%d  2			- int part
> 	%2d __2 		- filled int part (_'s represent spaces)
> 	%.2d 2.13 		- ?rounded? to two places
> 	%2.2d __2.13	- filled and ?rounded? to 2 places
> 	%2.5d __2.13500	- filled and zero extended
> 	%.3d 2.135 		- etc.

Hooks would have to be added to the core to support any of that.  Piles of
new features requiring core changes are out of scope for 2.3 at this stage.

> ...
> The '%s' output just looks suspicious, especially after setting the
> precision to 4! The '%s' smells of bug to me, although I see the
> code forces the initializer value to round to DEFAULT_PRECISION if a
> precision isn't given ("you lost my work!"). IMO the precision of the
> initializer should become the precision of the value and not the
> default if a (seems redundant to me) precision isn't given.

floats have no inherent precision in this sense, and the number you passed
to the constructor was not decimal 2.135.  Appendix A of the Tutorial
explains this; the number you actually passed was

    1201898150554501 / 2**49 ==
    2.1349999999999997868371792719699442386627197265625

and FixedPoint rounded that correctly to 2 decimal digits after the radix
point.  You would have been more surprised if FixedPoint had said the
precision was 49, but it does require exactly 49 digits after the decimal
point to capture your input number without loss of information.

>>> print FixedPoint.FixedPoint(2.135, 51)
2.134999999999999786837179271969944238662719726562500

>>> print FixedPoint.FixedPoint("2.135", 51)
2.135000000000000000000000000000000000000000000000000

Now forget literals -- they really aren't very interesting.  You normally
have computed quantities, and there's no hope of *guessing* "the inherent
precision" from those, unless they're also FixedPoints and so explicitly
support a reasonable notion of precision.  You can't guess this from a float
in isolation, and you can't guess anything about how many digits after the
decimal point are implied by an integer.  You either accept the
constructor's default (2), or explicitly tell it how much precision you
believe the input has.