[Cython] Cygwin: Handling missing C99 long double functions
Erik Bray
erik.m.bray at gmail.com
Wed May 4 11:01:03 EDT 2016
On Tue, May 3, 2016 at 7:15 PM, Robert Bradshaw <robertwb at gmail.com> wrote:
> On Tue, May 3, 2016 at 3:04 AM, Erik Bray <erik.m.bray at gmail.com> wrote:
>>
>> On Thu, Apr 28, 2016 at 9:29 AM, Robert Bradshaw
>> <robertwb at math.washington.edu> wrote:
>> > On Wed, Apr 27, 2016 at 3:07 AM, Erik Bray <erik.m.bray at gmail.com>
>> > wrote:
>> >>
>> >> On Tue, Apr 26, 2016 at 10:55 PM, Robert Bradshaw <robertwb at gmail.com>
>> >> wrote:
>> >> > On Tue, Apr 26, 2016 at 8:36 AM, Erik Bray <erik.m.bray at gmail.com>
>> >> > wrote:
>> >> >>
>> >> >> On Tue, Apr 26, 2016 at 5:16 PM, Dima Pasechnik
>> >> >> <dimpase+github at gmail.com> wrote:
>> >> >> > Hi,
>> >> >> > certainly we did something with Sage on cygwin to work around
>> >> >> > these...
>> >> >> > Just in case,
>> >> >>
>> >> >> From what I can tell there are several places where Sage has hacked
>> >> >> around this issue in different packages, but it's not doing anything
>> >> >> specifically with it for Cython. Sage uses the Cephes math lib to
>> >> >> support these functions on FreeBSD--and apparently used to use it on
>> >> >> Cygwin too, but disabled that for some reason.
>> >> >>
>> >> >> Regardless, Cython should ultimately do something sensible in these
>> >> >> cases.
>> >> >>
>> >> >> My general thinking is that in cases where Cython generates code
>> >> >> containing C math functions, it ought to support a fallback. This
>> >> >> will require some feature checks so that Cython can generate
>> >> >> wrappers,
>> >> >> when necessary, around the double versions of those functions (as
>> >> >> Numpy currently does).
>> >> >
>> >> >
>> >> > Let's make things concrete. You're complaining that something like
>> >> >
>> >> > cdef extern from "math.h":
>> >> > long double sqrtl(long double)
>> >> >
>> >> > def foo(long double x):
>> >> > return sqrtl(x)
>> >> >
>> >> > Doesn't work on Cygwin?
>> >> >
>> >> > The same is true for *any* C function that you use that's not totally
>> >> > portable (this is the bane of trying to use C). I don't think Cython
>> >> > should
>> >> > be detecting this and substituting a (less accurate) sqrt for sqrtl
>> >> > in
>> >> > this
>> >> > case. If you want do do this, write your own headers that
>> >> > (conditionally)
>> >> > define these things however you want.
>> >>
>> >> No, not at all. That would be silly. Let me be clearer...
>> >>
>> >> > Or, are you complaining that Cython's test suite doesn't pass on some
>> >> > Cygwin
>> >> > because there are tests of features not available on Cygwin? (Your
>> >> > original
>> >> > email isn't clear.) If so, the test framework can be set up to
>> >> > exclude
>> >> > these
>> >> > tests on that platform.
>> >>
>> >> There are really two concerns. This is one of them, yes. The first
>> >> question is what would be the best way to exclude certain tests on
>> >> certain platforms? There's no clear documentation on that (which is
>> >> fine, but it's why I'm asking :)
>> >
>> >
>> > Probably add a tag and then modify runtests.py to detect Cygwin and
>> > automatically add this to the exclusion list.
>>
>> Okay.
>>
>> >> The second concern, and more serious, is that there *are* cases where
>> >> Cython generates code that uses long double functions where, for
>> >> example, long double is passed as an argument. For example the
>> >> following code
>> >>
>> >> def truncate_long_double(long double x):
>> >> cdef float r = int(x)
>> >> return r
>> >>
>> >> compiles to something that includes:
>> >>
>> >> /* "truncl.pyx":2
>> >> * def truncate_long_double(long double x):
>> >> * cdef float r = int(x) # <<<<<<<<<<<<<<
>> >> * return r
>> >> */
>> >> __pyx_t_1 = truncl(__pyx_v_x);
>> >> __pyx_v_r = __pyx_t_1;
>> >>
>> >> /* "truncl.pyx":3
>> >> * def truncate_long_double(long double x):
>> >> * cdef float r = int(x)
>> >> * return r # <<<<<<<<<<<<<<
>> >> */
>> >> __Pyx_XDECREF(__pyx_r);
>> >> __pyx_t_2 = PyFloat_FromDouble(__pyx_v_r); if
>> >> (unlikely(!__pyx_t_2)) __PYX_ERR(0, 3, __pyx_L1_error)
>> >>
>> >> It's not not clear what the best way would be to *not* use this code
>> >> on platforms where truncl missing (and there are a handful of other
>> >> examples besides truncl). This code is generated by an optimization
>> >> that was added here: http://trac.cython.org/ticket/400 In this case
>> >> replacing truncl with trunc shouldn't hurt.
>> >
>> >
>> > Here, yes, but in general truncl(x) != trunc(x) for, say, 2**54 + 1.
>> >
>> >> It's not uncommon (I think) to ship pre-cythonized C sources in a
>> >> package's source distribution so that it can be installed (especially
>> >> by pip) without requiring the user to have Cython. This code will
>> >> fail to compile on platforms like Cygwin.
>> >>
>> >> But it's not clear how to handle this at Cythonization time either
>> >> since it doesn't know in advance what platform it will be targeting.
>> >> I'm suggesting that maybe rather than using these functions directly
>> >> Cython should generate some fallbacks for where they're not available,
>> >> or at least have the option to.
>> >
>> >
>> > The safest is to never use the long double type in this case--we assume
>> > that
>> > if long double is present (used), so are the corresponding functions in
>> > math.h (which is wrong for this platform, but if we need to use powl I
>> > don't
>> > know what else to do). Alternatively, ship your own (conditionally
>> > defined)
>> > fallbacks.
>>
>> I agree that it would be safest not to use long double where long
>> double functions are not support, and an application that does should
>> probably check for that.
>>
>> The problem is that Cython is generating code that simply *cannot* be
>> compiled on such platforms.
>
>
> Does Cython ever generate such code when long doubles are not used in the
> source? If so, we should fix that. But I don't think Cython should be
> swapping in double versions for long double operations.
No, but the problem is that Cython is a pretty heavy layer of
abstraction. In the example I gave no long double function was ever
used explicitly--it was added by a compiler optimization. I probably
wouldn't write that code thinking that I will need a workaround on
platforms that don't have truncl.
In the slightly more explicit cases of fmod and pow I might agree.
Though there still isn't a great way to anticipate the need to write
separate handling for long double functions into a module, for a
platform that doesn't support them. I can't write something in a
Cython module like:
if sys.platform != 'cygwin':
def my_long_double_func(long double x):
....
Maybe would be nice to have a simple compiler directive that outright
disables compiling a function depending on a platform check. (I think
this would also be useful for the test framework--the current system
of module-level tags is not fine-grained as I would like, I'm finding.
>> My suggestion would be to replace the
>> current calls to [trunc,fmod,pow]l to the appropriate
>> __Pyx_%(func)_%(type) wrapper which would provide two alternatives:
>> One that works properly, and one that falls back to using the double
>> version of the function (Numpy does this so there is some precedent).
>> I would go further and suggest throwing up a pre-compiler warning when
>> using the double versions.
>>
>> The only question is what condition (in my mind) is what to use as a
>> condition for selecting the fallbacks. For now I'm thinking something
>> quite specific like
>>
>> #if defined(__CYGWIN__) && defined(_LDBL_EQ_DBL)
>>
>> since I'm apparently the only person who cares about this at the
>> moment, and Cygwin is the platform I'm targeting.
>>
>> Again, my only real concern right now is that Cython generates code
>> that can be compiled on Cygwin.
>
>
> If you want this behavior, wouldn't including a header
>
> #if defined(__CYGWIN__) && defined(_LDBL_EQ_DBL)
> #define truncl trunc
> #define fmodl fmod
> #define powl pow
> #endif
>
> as part of your package do the job?
That's a reasonable workaround. But I would argue that should be baked
into Cythonized code, at least optionally, and should generate a
warning.
Best,
Erik
More information about the cython-devel
mailing list