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
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
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 ->