Hi Miller,

My intention is to write Pd code in such a way that it is maximally efficient for tiny computers which aren't fond of doubles (RPi and friends), while keeping the option open to compile with maximum precision for fast machines.

The advent of RPi has prompted me to focus more on efficiency, even though I rarely use a Pi myself. Reckoning with ARM, I guess we're sort of back in the days of Pentium 2 when it comes to precious clock cycles (but I didn't program C at that time).

Overlooked precision conversions happen so easily. Here is an example from tabread4~ in d_array.c:       

      *out++ = b + frac * (
            cminusb - 0.1666667f * (1.-frac) * (
                (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b)
            )
        );

All literals have float suffixes, except in (1.-frac). So here is a double which makes the compiler do the first half of the interpolation routine on the FPU with extended precision (on Linux i386).

A few years ago I wouldn't have noticed it, and my own code my be full of unspecified literals. That's what I want to repair now. Not by adding the regular float specifier 'f' though, since that would defy the purpose of Pd's own float type definition.

Katja


On Tue, Feb 11, 2014 at 6:39 AM, Miller Puckette <msp@ucsd.edu> wrote:
Hi Katya -

I think there's no simpler way.  On the other hand, for constants like
0.125 and 2, it would be equivalent to say 0.125f, etc - but for other
constants (1/3 for example), casting as t_float would be more accurate in
case t_float is set to double.  I think people rarely use t_float as higher
precision than 32 bits though, and even if they did the difference between
(t_float)1/(t_float)3 and 1.f/3.f is pretty small.

cheers
Miller

On Mon, Feb 10, 2014 at 10:53:02PM +0100, katja wrote:
> Hello,
>
> When working on parabolic interpolation in a Pd class, I wondered again
> what is the best method to specify literal constants as Pd's type t_float
> (which could be float or double). The interpolation goes like:
>
>     ...
>     t_float a = buf[peakindex-1];
>     t_float b = buf[peakindex];
>     t_float c = buf[peakindex+1];
>     t_float realpeak;
>
>     realpeak = b + 0.125 * (c - a) * (c - a) / (2. * b - a - c);
>     ...
>
> Without float suffixes for the literals, single precision t_float variables
> would be promoted to double here, which would be an unintended waste of CPU
> cycles. For some time, I've worked around this by using const variables
> instead of literals, like:
>
>     ...
>     const t_float two = 2.;
>     const t_float eighth = 0.125;
>     t_float a = buf[peakindex-1];
>     t_float b = buf[peakindex];
>     t_float c = buf[peakindex+1];
>     t_float realpeak;
>
>     realpeak = b + eighth * (c - a) * (c - a) / (two * b - a - c);
>     ...
>
> While this avoids redundant type conversions, it clutters the code and does
> not result in such fast instructions as literals do. Therefore I am now
> using type casts where type specifiers are normally used:
>
>     ...
>     t_float a = buf[peakindex-1];
>     t_float b = buf[peakindex];
>     t_float c = buf[peakindex+1];
>     t_float realpeak;
>
>     realpeak = b + (t_float)0.125 * (c - a) * (c - a) / ((t_float)2. * b -
> a - c);
>     ...
>
> For the above code I have checked assembly output as generated by GCC with
> -O3 optimization on Linux i386. Using literals without type specification,
> the whole routine is done on the FPU (80 bits precision). With the literals
> cast to t_float, it is done with single precision instructions for XMM
> registers.
>
> As far as I can see, casting literals to t_float results in the same
> assembly output as using the float specifier. For single precision
> t_float,  '(t_float)0.125' is equivalent to '0.125f'. I can't think of a
> disadvantage, but let me know if I overlooked something.
>
> Katja

> _______________________________________________
> Pd-list@iem.at mailing list
> UNSUBSCRIBE and account-management -> http://lists.puredata.info/listinfo/pd-list