mailing list of musl libc
 help / color / mirror / code / Atom feed
* libm
@ 2012-01-23 16:41 Szabolcs Nagy
  2012-01-23 17:07 ` libm Rich Felker
  0 siblings, 1 reply; 28+ messages in thread
From: Szabolcs Nagy @ 2012-01-23 16:41 UTC (permalink / raw)
  To: musl

i've looked into libm implementations
to figure out what's best for musl

some resources:
    http://nsz.repo.hu/libm.txt

there are various issues:

* reproducible semantics:

linux uses double extended precision by default on i386
which does not give the same results as standard 64bit
ieee754 (sse2, x86_64, arm)

bsd uses plain double precision by default on i386
(so at least when bsd functions are ported the precision
claims should be reevaluated)

most of the recent advances in efficient and correct
function evaluation assume strict ieee754 64bit arithmetics
(so they can do formal proofs etc, some algorithms assume
fma instruction which is not available on musl's targets)

portable semantics are not easy to guarantee (extended
precision, rounding modes, compiler optimizations)
(especially if efficiency is important as well)

* tradeoffs:

modern libms (libmcr, libultim, crlibm) try to guarantee
correctness but arg reduction and faithful (or even
correct) rounding is hard to do and hard to verify

some libms prefer simple implementation (eg the go math
package sincos is from cephes and starts to get inaccurate
when |x| > 2^30, but the implementation is much simpler
than the arg reduction in fdlibm)

in theory one should prefer correctness instead of size
and speed, but there are significant differences in code
size and implementation effort
the speed of a correct implementation can be good in
average case, but not in worstcase

* libms in practice:

many functions are the same in glibc and the various
bsd libcs (they are mostly based on fdlibm, but glibc
64bit double precision functions are from the gpl
licensed libultim)

the extended precision algorithms are reused across
different libcs as well, but there are 80bit vs 128bit
differences. the correctness of extended precision
algorithms are much less studied (eg there are no
correctly rounded versions or worstcases for 2pi arg
reductions)

most of the complex functions are simple once elementary
functions are there (bsd libcs has bsd licensed
implementations of these)


conclusion:

the simplest approach for musl at this point is to
reuse the already available math functions
(+run the available tests on them)

this can be done by diffing the various bsd and glibc
functions and choosing the best one (usually they are
the same except code organization and bit manipulation
details)

most of the task is understanding floating point
semantics well (not the mathematics of the algorithms)
and doing code cleanups

code and ideas from crlibm might be possible to use
but that would take much more effort and knowledge
(assuming we want small code size)

designing new algorithms for elementary functions seems
to require a huge effort and the best tradoffs are not
even clear


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-01-23 16:41 libm Szabolcs Nagy
@ 2012-01-23 17:07 ` Rich Felker
  2012-01-23 19:12   ` libm Szabolcs Nagy
  2012-02-27 21:02   ` libm Szabolcs Nagy
  0 siblings, 2 replies; 28+ messages in thread
From: Rich Felker @ 2012-01-23 17:07 UTC (permalink / raw)
  To: musl

On Mon, Jan 23, 2012 at 05:41:52PM +0100, Szabolcs Nagy wrote:
> i've looked into libm implementations
> to figure out what's best for musl

Thanks!!

> * reproducible semantics:

Unless others are deeply committed to having results match other
platforms/libcs bit-for-bit, this is not a major concern for me.
I'm happy with bit-exact just for non-transcendental (i.e. sqrt) and
1ulp (or even several ulp) max error for transcendental functions.

> * tradeoffs:
> 
> modern libms (libmcr, libultim, crlibm) try to guarantee
> correctness but arg reduction and faithful (or even
> correct) rounding is hard to do and hard to verify

Correct arg reduction is important to me. Incorrect arg reduction is
equivalent to billions of billions of ulp error in the final results.

> * libms in practice:
> 
> many functions are the same in glibc and the various
> bsd libcs (they are mostly based on fdlibm, but glibc
> 64bit double precision functions are from the gpl
> licensed libultim)

You really mean double, not extended? That's odd since fdlibm covers
double.. I wonder when/why they switched.

> the extended precision algorithms are reused across
> different libcs as well, but there are 80bit vs 128bit
> differences. the correctness of extended precision
> algorithms are much less studied (eg there are no
> correctly rounded versions or worstcases for 2pi arg
> reductions)

Any ideas how the different ones evolved (separately written or common
ancestor code, etc.) and where we should look to pull code from?

> most of the complex functions are simple once elementary
> functions are there (bsd libcs has bsd licensed
> implementations of these)

Well they don't have any more difficult numeric analysis issues, but
there are quite a few corner cases with complex functions which many
platforms have gotten wrong (issues with branch cuts and signed zeros,
etc.).

> conclusion:
> 
> the simplest approach for musl at this point is to
> reuse the already available math functions
> (+run the available tests on them)

I generally agree, provided no major bugs are found - and even if they
are, they should be fixable.

> this can be done by diffing the various bsd and glibc
> functions and choosing the best one (usually they are
> the same except code organization and bit manipulation
> details)

That was my impression too.

> code and ideas from crlibm might be possible to use
> but that would take much more effort and knowledge
> (assuming we want small code size)

Yes, crlibm looks very interesting but also very large...

> designing new algorithms for elementary functions seems
> to require a huge effort and the best tradoffs are not
> even clear

Agreed. I think it's roughly as big a task as the rest of musl
combined, so it's best not to go there.

Rich


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-01-23 17:07 ` libm Rich Felker
@ 2012-01-23 19:12   ` Szabolcs Nagy
  2012-01-27 16:02     ` libm Szabolcs Nagy
  2012-02-27 21:02   ` libm Szabolcs Nagy
  1 sibling, 1 reply; 28+ messages in thread
From: Szabolcs Nagy @ 2012-01-23 19:12 UTC (permalink / raw)
  To: musl

* Rich Felker <dalias@aerifal.cx> [2012-01-23 12:07:15 -0500]:
> On Mon, Jan 23, 2012 at 05:41:52PM +0100, Szabolcs Nagy wrote:
> > * tradeoffs:
> > 
> > modern libms (libmcr, libultim, crlibm) try to guarantee
> > correctness but arg reduction and faithful (or even
> > correct) rounding is hard to do and hard to verify
> 
> Correct arg reduction is important to me. Incorrect arg reduction is
> equivalent to billions of billions of ulp error in the final results.
> 

yes, but trigonometric arg reduction is a real pain
and ppl shouldn't use large arguments there anyway..

so there are arguments both ways
but this is already reasonably
solved in fdlibm

> > * libms in practice:
> > 
> > many functions are the same in glibc and the various
> > bsd libcs (they are mostly based on fdlibm, but glibc
> > 64bit double precision functions are from the gpl
> > licensed libultim)
> 
> You really mean double, not extended? That's odd since fdlibm covers
> double.. I wonder when/why they switched.
> 

i forgot to mention that both the bsds and glibc have most of the
functions implemented in i387 asm (using the builtin instructions),
i'm not sure how far musl wants to take that

the generic 64bit double c code of glibc is from fdlibm and libultim
but libultim provides the important elementary functions (exp, log, pow,
sin, atan,..) while fdlibm the more obscure or simple ones (floor, rint,
fabs, cosh, tanh, j0, erf,..)

$ grep '* IBM' glibc/sysdeps/ieee754/dbl-64/*.c |wc -l
25
$ grep 'Copyright .* Sun' glibc/sysdeps/ieee754/dbl-64/*.c |wc -l
33

the libultim functions provide correct rounding but can be slow
(they use multiprecision arithmetic for various things and
eg for arg reduction they increase the precision until it's enough)

> > the extended precision algorithms are reused across
> > different libcs as well, but there are 80bit vs 128bit
> > differences. the correctness of extended precision
> > algorithms are much less studied (eg there are no
> > correctly rounded versions or worstcases for 2pi arg
> > reductions)
> 
> Any ideas how the different ones evolved (separately written or common
> ancestor code, etc.) and where we should look to pull code from?
> 

most of the available *l (and *f) code is based on fdlibm
but ported by someone to long double (or float)

openbsd and freebsd mostly use the same code by the same contributors
as far as i can see, but they have separate 80bit and 128bit variants
for important elementary functions

glibc is sometimes the same as the bsds but often not (eg expl and
sinl are clearly different)

i don't think there are other available widely used implementations
so we have openbsd, freebsd and glibc
(mathcw has long double functions but i don't see source code and
it does not seem to be active)


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-01-23 19:12   ` libm Szabolcs Nagy
@ 2012-01-27 16:02     ` Szabolcs Nagy
  2012-01-27 19:01       ` libm Pascal Cuoq
  0 siblings, 1 reply; 28+ messages in thread
From: Szabolcs Nagy @ 2012-01-27 16:02 UTC (permalink / raw)
  To: musl

* Szabolcs Nagy <nsz@port70.net> [2012-01-23 20:12:15 +0100]:
> * Rich Felker <dalias@aerifal.cx> [2012-01-23 12:07:15 -0500]:
> > On Mon, Jan 23, 2012 at 05:41:52PM +0100, Szabolcs Nagy wrote:
> > > many functions are the same in glibc and the various
> > > bsd libcs (they are mostly based on fdlibm, but glibc
> > > 64bit double precision functions are from the gpl
> > > licensed libultim)
> > 
> > You really mean double, not extended? That's odd since fdlibm covers
> > double.. I wonder when/why they switched.
> > 
> 

sorry libultim is lgpl
the source tree contains a gpl license for some reason, but all
source comments say lgpl, both in glibc and the original files

meanwhile i compared some implementations
openbsd, freebsd and glibc are worth to look at
(netbsd and bionic libms seem to be less maintained)


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-01-27 16:02     ` libm Szabolcs Nagy
@ 2012-01-27 19:01       ` Pascal Cuoq
  2012-01-27 19:34         ` libm Rich Felker
  0 siblings, 1 reply; 28+ messages in thread
From: Pascal Cuoq @ 2012-01-27 19:01 UTC (permalink / raw)
  To: musl

[-- Attachment #1: Type: text/plain, Size: 1496 bytes --]

On Fri, Jan 27, 2012 at 5:02 PM, Szabolcs Nagy <nsz@port70.net> wrote:

> meanwhile i compared some implementations
> openbsd, freebsd and glibc are worth to look at
>

Sorry to intrude in your conversation, but for reasons of
my own I was recently looking for an aesthetically pleasing
single-precision implementation of trigonometric functions,
and some of the single-precisions functions out there are
so ugly that I would seriously consider implementing
these in musl by a call to their respective double-precision
counterparts, despite the disadvantages. At least it's compact.

Consider:
http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libm/src/k_cosf.c?rev=1.3;content-type=text%2Fplain

The code written at Sun and that many libms reuse must
have been double-precision only, and someone had to
make a single-precision version of them at some point.
Without the analysis that led to the original code, one
cannot blame Ian Lance Taylor for staying close
to the double-precision function:
http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libm/src/k_cos.c?rev=1.4;content-type=text%2Fplain

But the degree of the polynomial in the single-precision version
is about twice what is necessary, and the coefficients
look like coefficients of the Taylor expansion rounded to
nearest float, when they should be Chebyshev coefficients.

Criticism is easy and art is difficult. I don't know how
to write a better single-precision function, I just know
that this one, for instance, cannot be good.

Pascal

[-- Attachment #2: Type: text/html, Size: 2316 bytes --]

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-01-27 19:01       ` libm Pascal Cuoq
@ 2012-01-27 19:34         ` Rich Felker
  2012-01-29 16:34           ` libm Szabolcs Nagy
  0 siblings, 1 reply; 28+ messages in thread
From: Rich Felker @ 2012-01-27 19:34 UTC (permalink / raw)
  To: musl

On Fri, Jan 27, 2012 at 08:01:18PM +0100, Pascal Cuoq wrote:
> On Fri, Jan 27, 2012 at 5:02 PM, Szabolcs Nagy <nsz@port70.net> wrote:
> 
> > meanwhile i compared some implementations
> > openbsd, freebsd and glibc are worth to look at
> >
> 
> Sorry to intrude in your conversation, but for reasons of
> my own I was recently looking for an aesthetically pleasing
> single-precision implementation of trigonometric functions,
> and some of the single-precisions functions out there are
> so ugly that I would seriously consider implementing
> these in musl by a call to their respective double-precision
> counterparts, despite the disadvantages. At least it's compact.

*Nod*, this is the way I'm leaning for single-precision anyway. The
only possible reason to reconsider is that some systems (i.e. some
ARMs) have slow software emulation of double precision but native
single precision, or so I'm told...

> Consider:
> http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libm/src/k_cosf.c?rev=1.3;content-type=text%2Fplain
> 
> The code written at Sun and that many libms reuse must
> have been double-precision only, and someone had to
> make a single-precision version of them at some point.
> Without the analysis that led to the original code, one
> cannot blame Ian Lance Taylor for staying close
> to the double-precision function:
> http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libm/src/k_cos.c?rev=1.4;content-type=text%2Fplain
> 
> But the degree of the polynomial in the single-precision version
> is about twice what is necessary, and the coefficients
> look like coefficients of the Taylor expansion rounded to
> nearest float, when they should be Chebyshev coefficients.

Agreed, this is ridiculous.

> Criticism is easy and art is difficult. I don't know how
> to write a better single-precision function, I just know
> that this one, for instance, cannot be good.

Well I think it's fair to judge when they could have just wrapped the
double precision versions. Granted this can result in 1ulp error, but
it's not a big deal and within the tolerances required by the
standard...and if their implementations of the single-precision
versions are as bad as you say, I can't imagine they're always
correctly rounded anyway..

Rich


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-01-27 19:34         ` libm Rich Felker
@ 2012-01-29 16:34           ` Szabolcs Nagy
  0 siblings, 0 replies; 28+ messages in thread
From: Szabolcs Nagy @ 2012-01-29 16:34 UTC (permalink / raw)
  To: musl

* Rich Felker <dalias@aerifal.cx> [2012-01-27 14:34:24 -0500]:
> On Fri, Jan 27, 2012 at 08:01:18PM +0100, Pascal Cuoq wrote:
> > On Fri, Jan 27, 2012 at 5:02 PM, Szabolcs Nagy <nsz@port70.net> wrote:
> > 
> > > meanwhile i compared some implementations
> > > openbsd, freebsd and glibc are worth to look at
> > >
> > 
> > Sorry to intrude in your conversation, but for reasons of
> > my own I was recently looking for an aesthetically pleasing
> > single-precision implementation of trigonometric functions,
> > and some of the single-precisions functions out there are
> > so ugly that I would seriously consider implementing
> > these in musl by a call to their respective double-precision
> > counterparts, despite the disadvantages. At least it's compact.
> 
> *Nod*, this is the way I'm leaning for single-precision anyway. The
> only possible reason to reconsider is that some systems (i.e. some
> ARMs) have slow software emulation of double precision but native
> single precision, or so I'm told...
> 

yes, this is good for code sizeand maintainability

some code will expect faster execution when it uses
the float versions so some benchmarking will be needed
for arm to see the tradeoffs

> > Consider:
> > http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libm/src/k_cosf.c?rev=1.3;content-type=text%2Fplain
> > 
> > The code written at Sun and that many libms reuse must
> > have been double-precision only, and someone had to
> > make a single-precision version of them at some point.
> > Without the analysis that led to the original code, one
> > cannot blame Ian Lance Taylor for staying close
> > to the double-precision function:
> > http://www.openbsd.org/cgi-bin/cvsweb/src/lib/libm/src/k_cos.c?rev=1.4;content-type=text%2Fplain
> > 
> > But the degree of the polynomial in the single-precision version
> > is about twice what is necessary, and the coefficients
> > look like coefficients of the Taylor expansion rounded to
> > nearest float, when they should be Chebyshev coefficients.
> 
> Agreed, this is ridiculous.

bruce d evans in freebsd fixed these

http://svnweb.FreeBSD.org/base/stable/9/lib/msun/src/k_cosf.c?view=log
see rev 152951 and 152869

openbsd is a bit behind freebsd libm
improvements in some places
but freebsd misses some long double
functions implemented in openbsd
that's why i recommended looking into
both of them



^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-01-23 17:07 ` libm Rich Felker
  2012-01-23 19:12   ` libm Szabolcs Nagy
@ 2012-02-27 21:02   ` Szabolcs Nagy
  2012-02-27 22:24     ` libm Rich Felker
  1 sibling, 1 reply; 28+ messages in thread
From: Szabolcs Nagy @ 2012-02-27 21:02 UTC (permalink / raw)
  To: musl

* Rich Felker <dalias@aerifal.cx> [2012-01-23 12:07:15 -0500]:
> On Mon, Jan 23, 2012 at 05:41:52PM +0100, Szabolcs Nagy wrote:
> > i've looked into libm implementations
> > to figure out what's best for musl
> 
> > the extended precision algorithms are reused across
...
> 
> Any ideas how the different ones evolved (separately written or common
> ancestor code, etc.) and where we should look to pull code from?
> 

meanwhile i looked more into libm design issues

here are some questions i come up with:
for background issues see
http://nsz.repo.hu/libm

Code organization:
(ldX is X bit long double)

Do we want ld128?
Should we try to use common code for ld80 and ld128?
How to do ld64: wrap double functions or alias them?
How to tie the ld* code to the arch in the build system?
Make complex optional?
Keep complex in math/ or cmath/?

Workarounds:

Use STDC pragmas (eventhough gcc does not support them)?
Use volatile consistently to avoid evaluation precision and const folding issues?
Use special compiler flags against unwanted optimization (-frounding-math, -ffloat-store)?
Do inline/macro optimization for small functions? (isnan, isinf, signbit, creal, cimag,..)
In complex code prefer creal(), cimag() or a union to (un)pack re,im?

Code cleanups:

Keep diffability with freebsd/openbsd code or reformat the code for clarity?
Keep e_, s_, k_ fdlibm source file name prefixes?
Should 0x1p0 float format be preferred over decimal format?
Should isnan, signbit,.. be preferred over inplace bithacks?
Is unpacking a double into 2 int32_t ok (signed int representation)?
Is unpacking the mantissa of ld80 into an int64_t ok?



^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-02-27 21:02   ` libm Szabolcs Nagy
@ 2012-02-27 22:24     ` Rich Felker
  2012-03-03 22:57       ` libm Szabolcs Nagy
  0 siblings, 1 reply; 28+ messages in thread
From: Rich Felker @ 2012-02-27 22:24 UTC (permalink / raw)
  To: musl

On Mon, Feb 27, 2012 at 10:02:53PM +0100, Szabolcs Nagy wrote:
> * Rich Felker <dalias@aerifal.cx> [2012-01-23 12:07:15 -0500]:
> > On Mon, Jan 23, 2012 at 05:41:52PM +0100, Szabolcs Nagy wrote:
> > > i've looked into libm implementations
> > > to figure out what's best for musl
> > 
> > > the extended precision algorithms are reused across
> ....
> > 
> > Any ideas how the different ones evolved (separately written or common
> > ancestor code, etc.) and where we should look to pull code from?
> > 
> 
> meanwhile i looked more into libm design issues
> 
> here are some questions i come up with:
> for background issues see
> http://nsz.repo.hu/libm
> 
> Code organization:
> (ldX is X bit long double)
> 
> Do we want ld128?

Only for systems where long double is 128-bit, and so far, we don't
support any such systems. I'd say it's very low priority right now.

> Should we try to use common code for ld80 and ld128?

This would be nice but I doubt it's feasible, especially without a lot
of divergence from upstream.

> How to do ld64: wrap double functions or alias them?

Wrap. Alias is non-conformant; distinct functions visible to the
application (and not in the reserved namespace) must not have the same
address. I don't mind bending this rule for non-standard functions
that just exist as weak aliases for compatibility with legacy systems,
since conformant applications cannot use them anyway, but for standard
functions this is really a conformance issue. Hopefully the wrapers
will compile to a single tail-call jump instruction anyway.

> How to tie the ld* code to the arch in the build system?

Just put all the files in place, and put #if LDBL_MANT_DIG==... etc.
in the files to make them compile to empty .o files on platforms where
they're not needed.

> Make complex optional?

At this point I don't see a good reason to make anything optional. For
static linking it won't get pulled in unnecessarily anyway, and for
dynamic linking, the whole libc.so is still very small. If size
becomes an issue in the future, I'd like a more generic way of
choosing what symbols to link into the .so at link time rather than
adding lots of #ifdef to the source.

> Keep complex in math/ or cmath/?

I'm fairly indifferent to the issue, especially since with the above
comments in mind it shouldn't affect the build system. If math/ is too
cluttered already, cmath/ may be nice.

> Workarounds:
> 
> Use STDC pragmas (eventhough gcc does not support them)?

There may be another -f option we need to make gcc extra-attentive to
standards for floating point...

> Use volatile consistently to avoid evaluation precision and const
> folding issues?

This might be a nice extra precaution, and might be best if we can't
find any other consistently-working fix.

> Use special compiler flags against unwanted optimization
> (-frounding-math, -ffloat-store)?

I think these are what we want. My understanding is that they behave
similarly to the STDC pragmas. If we add both the STDC pragmas (which
gcc will ignore) and -frounding-math and -ffloat-store, then either
GCC or a C99 conformant compiler should generate correct code.

> Do inline/macro optimization for small functions? (isnan, isinf,
> signbit, creal, cimag,..)
> In complex code prefer creal(), cimag() or a union to (un)pack re,im?

My preference would be to use the standard function-like-macro names
(isnan, creal, etc.) internally. They should expand to something just
as efficient as the union unpacking stuff used internally, but
actually be portable and clear to somebody reading the code.

Of course if it's a lot of gratuitous changes from upstream I'd be
willing to leave it alone, as long as the upstream code is not
invoking UB.

> Code cleanups:
> 
> Keep diffability with freebsd/openbsd code or reformat the code for
> clarity?

> Keep e_, s_, k_ fdlibm source file name prefixes?

I'm fairly indifferent on the matter.

> Should 0x1p0 float format be preferred over decimal format?

I would prefer hex floats so the exact value is clear and so there's
no danger of toolchain bugs causing the constants to be miscompiled.

> Should isnan, signbit,.. be preferred over inplace bithacks?

Yes, but see above..

> Is unpacking a double into 2 int32_t ok (signed int representation)?

Personally I find it really ugly, especially since it introduces an
endian dependency. And there may be UB issues with signed overflow
unless the code is very careful. uint64_t is the ideal unpacking
format for double.

> Is unpacking the mantissa of ld80 into an int64_t ok?

Yes, and this has an unavoidable endian issue... Actually uint64_t
would be better. A valid, finite, normal ld80 will always be negative
as a signed integer, by the way (because the leading 1 is mandatory
but not implicit).

Rich


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-02-27 22:24     ` libm Rich Felker
@ 2012-03-03 22:57       ` Szabolcs Nagy
  2012-03-04  6:53         ` libm Rich Felker
  0 siblings, 1 reply; 28+ messages in thread
From: Szabolcs Nagy @ 2012-03-03 22:57 UTC (permalink / raw)
  To: musl

see
http://nsz.repo.hu/libm

i put together a libm using the freebsd libm
and some code from openbsd

i did various useless code formatting cleanups
as the code was in bad shape (we will have to
rewrite some parts anyway, so it is not that
important to keep fdlibm diffability, instead
i recorded the date of the freebsd and openbsd
source tree checkout so we can see if they fixed
something since then)

the // FIXME and TODO comments are from me
(i did not mark all the issues, i got tired after a while)


notes:

the source contains parts of musl to make testing/building
easy

i modified math.h and added complex.h and tgmath.h
(although i'm not sure about the later, it uses c11)

i already rewrote some simple functions when they were
incorrect (ilogbl) or overcomplicated (fmax, logb)

internal stuff is in libm.h which includes math.h
and float.h so every .c just includes libm.h

long double is handled in an ugly way now:
ldhack.h contains all the ld macros and unions
that is used in the *l.c functions
for my convenience i defined LD64, LD80 and LD128
there and just used these with #if directive

complex is missing

tgammaf, tgamma is missing
(the only 3clause bsd licensed code)
(cephes lib has another implementation)

i added frexp from musl stdlib for completeness

i did some testing of the double functions
using crlibm and ucbtest testvectors
and so far things seem fine
(pow has some issues in certain corner cases
and it seems x87 hardware sqrt is not correctly
rounded eventhough ieee requires it to be,
or maybe it's another double rounding issue)


questions:

keep frexp in stdlib?

how to do the long double ifdefs?

check x87 fpu precision setting from ld80 code?
(or we assume it's extended precision)

preferred way of handling consts?
(freebsd code uses 1.0f, (float)1, 0x1p0, static const float one=1;..)
(i'm not sure why 'one' or 'zero' is used instead of 1 or 0
maybe it's another compiler float-store bug workaround)
i assume (float)1.0 is the same as 1f

i wonder if it's ok to use a polyeval(coeffs, x) function instead
of writing out the polynomial (some openbsd long double code uses
such thing but not everywhere)


todo:
- move the code to musl tree so the changes are recorded there
- fix int32_t issues (there are many)
- define isnan, signbit, etc macros to be efficient and
change bithacks into isnan etc in the code when appropriate
- fix extended precision issues (probably only fma)

there are lower priority tasks:
use scalbn instead of ldexp in internal code (ldexp is a wrapper)
clean up hard to follow control flows
fix invalid comments
clean up long double code
use consts consistently
make proper libm test suit


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-03 22:57       ` libm Szabolcs Nagy
@ 2012-03-04  6:53         ` Rich Felker
  2012-03-04 14:50           ` libm Szabolcs Nagy
  0 siblings, 1 reply; 28+ messages in thread
From: Rich Felker @ 2012-03-04  6:53 UTC (permalink / raw)
  To: musl, nsz

On Sat, Mar 03, 2012 at 11:57:58PM +0100, Szabolcs Nagy wrote:
> see
> http://nsz.repo.hu/libm

Some initial questions...

- Why are there two versions (one "slow") of rem_pio2?

- Is __invtrigl.c used? It clashes with the app-reserved namespace.

> i did various useless code formatting cleanups
> as the code was in bad shape (we will have to
> rewrite some parts anyway, so it is not that
> important to keep fdlibm diffability, instead
> i recorded the date of the freebsd and openbsd
> source tree checkout so we can see if they fixed
> something since then)

Sounds fine to me.

> i modified math.h and added complex.h and tgmath.h
> (although i'm not sure about the later, it uses c11)

There's an old gcc way to do tgmath.h without c11, but it's full of
gcc-specific hacks involving __typeof__. If gcc4 allows _Generic even
with -std=c99 etc. then I think _Generic is the best.

> questions:
> 
> keep frexp in stdlib?

It's ok to move it. I had it there from back when there was a separate
libm because printf depends on it.

> how to do the long double ifdefs?

#if LDBL_MANT_DIG == ...

> check x87 fpu precision setting from ld80 code?
> (or we assume it's extended precision)

All existing code (well, mainly macro definitions) in musl assumes its
set to extended, and we provide no code to change it.

> preferred way of handling consts?
> (freebsd code uses 1.0f, (float)1, 0x1p0, static const float one=1;..)
> (i'm not sure why 'one' or 'zero' is used instead of 1 or 0
> maybe it's another compiler float-store bug workaround)
> i assume (float)1.0 is the same as 1f

I like 1.0f, etc. BTW are you sure 1f is valid C? Certainly 1L is not
a valid alternative for 1.0L when you want long double..

> i wonder if it's ok to use a polyeval(coeffs, x) function instead
> of writing out the polynomial (some openbsd long double code uses
> such thing but not everywhere)

I suspect the functions that aren't using it right now depend on a
particular evaluation order to avoid loss of precision. I could be
wrong though; that's just my best guess.

> todo:
> - move the code to musl tree so the changes are recorded there

Very interested in doing this sooner rather than later.

> - fix int32_t issues (there are many)
> - define isnan, signbit, etc macros to be efficient and
> change bithacks into isnan etc in the code when appropriate

Try something like:

union __float_rep { float __x; uint32_t __r; };
#define __FLOAT_REP(x) ((union __float_rep){x}.__r)

#define isnan(x) \
	sizeof(x)==sizeof(float) ? (__FLOAT_REP(x)&0x7fffffff)>0x7f800000 : \
	sizeof(x)==sizeof(double) ? (__DOUBLE_REP(x)&....

Does that work? Of course it would work to just let it get converted
up implicitly to long double and just have one case, but that would be
more expensive I think.

Rich


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-04  6:53         ` libm Rich Felker
@ 2012-03-04 14:50           ` Szabolcs Nagy
  2012-03-04 18:43             ` libm Rich Felker
  2012-03-05 11:08             ` libm Szabolcs Nagy
  0 siblings, 2 replies; 28+ messages in thread
From: Szabolcs Nagy @ 2012-03-04 14:50 UTC (permalink / raw)
  To: musl

* Rich Felker <dalias@aerifal.cx> [2012-03-04 01:53:40 -0500]:
> On Sat, Mar 03, 2012 at 11:57:58PM +0100, Szabolcs Nagy wrote:
> > see
> > http://nsz.repo.hu/libm
> 
> Some initial questions...
> 
> - Why are there two versions (one "slow") of rem_pio2?
> 
originally code they are __ieee754_rem_pio2 and __kernel_rem_pio2
maybe slow is not the best name

the slow version is only called (by the non-slow one)
for large numbers (>2^20) when multi precision arithmetic
is needed using many bits of 2/pi
(there is a large ipio2 array in that file)

maybe it should be called __rem_pio2_large ?

> - Is __invtrigl.c used? It clashes with the app-reserved namespace.
> 
fixed
it is used by long double inv trig functions

> gcc-specific hacks involving __typeof__. If gcc4 allows _Generic even
> with -std=c99 etc. then I think _Generic is the best.
> 
no, gcc does not support it
(i have a gcc 4.7 built from svn in october and it has -std=c1x,
but _Generic is not supported)

i wrote it using generic since i couldnt come up with
any way to solve it in c99

> > how to do the long double ifdefs?
> 
> #if LDBL_MANT_DIG == ...
> 
ok will do that

> > check x87 fpu precision setting from ld80 code?
> > (or we assume it's extended precision)
> 
> All existing code (well, mainly macro definitions) in musl assumes its
> set to extended, and we provide no code to change it.
> 
ok

> > preferred way of handling consts?
> > (freebsd code uses 1.0f, (float)1, 0x1p0, static const float one=1;..)
> > (i'm not sure why 'one' or 'zero' is used instead of 1 or 0
> > maybe it's another compiler float-store bug workaround)
> > i assume (float)1.0 is the same as 1f
> 
> I like 1.0f, etc. BTW are you sure 1f is valid C? Certainly 1L is not
> a valid alternative for 1.0L when you want long double..
> 
ok
(1f might be wrong, i just wrote that in the mail)

> > i wonder if it's ok to use a polyeval(coeffs, x) function instead
> > of writing out the polynomial (some openbsd long double code uses
> > such thing but not everywhere)
> 
> I suspect the functions that aren't using it right now depend on a
> particular evaluation order to avoid loss of precision. I could be
> wrong though; that's just my best guess.
> 
it's just a minor detail, but this was one of the parts where
things could be a bit nicer and more consistent (probably)

(coeffs in array, and using some static inline eval etc,
it might not be a good idea for some technical reason,
but i dont yet see why)

> > todo:
> > - move the code to musl tree so the changes are recorded there
> 
> Very interested in doing this sooner rather than later.
> 
ok
complex is missing but it is not hard to add
(if you want to wait for it)

the parts i wrote may need some review or testing
(it can be done after merging of course)

(it would be nice if musl had a dedicated testing
package where one could add test functions
and easy to run)

> > - fix int32_t issues (there are many)
> > - define isnan, signbit, etc macros to be efficient and
> > change bithacks into isnan etc in the code when appropriate
> 
> Try something like:
> 
> union __float_rep { float __x; uint32_t __r; };
> #define __FLOAT_REP(x) ((union __float_rep){x}.__r)
> 
> #define isnan(x) \
> 	sizeof(x)==sizeof(float) ? (__FLOAT_REP(x)&0x7fffffff)>0x7f800000 : \
> 	sizeof(x)==sizeof(double) ? (__DOUBLE_REP(x)&....
> 
> Does that work? Of course it would work to just let it get converted
> up implicitly to long double and just have one case, but that would be
> more expensive I think.
>
ok so using compound literal
i was thinking about this
something like this will work

the difficult part is figuring out if it can be used internally

(many math functions start by turning the float/double
into unsigned int, then doing various checks to handle
special cases like nan, inf, signbit, etc using bithacks

i thought most of them could be isnan() etc
eg compare the current fmax to the freebsd one

i'm not sure if this can be done in general
because some bit hacks will be needed anyway
and it might be better doing the conversion
once

many functions do checks like
  if(x < 12.34) ... else if(x < 78.9) ...
but using unsigned int arithmetics like
  if ((x&bitmask) < 0xabcdef) ...
which is not clear, but i guess this
cannot be changed because float compare
does a different thing
but it would be nice to reduce the bithacks
without changing the semantics or performance)




^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-04 14:50           ` libm Szabolcs Nagy
@ 2012-03-04 18:43             ` Rich Felker
  2012-03-05  8:51               ` libm Szabolcs Nagy
  2012-03-05 11:08             ` libm Szabolcs Nagy
  1 sibling, 1 reply; 28+ messages in thread
From: Rich Felker @ 2012-03-04 18:43 UTC (permalink / raw)
  To: musl

On Sun, Mar 04, 2012 at 03:50:41PM +0100, Szabolcs Nagy wrote:
> > - Why are there two versions (one "slow") of rem_pio2?
> > 
> originally code they are __ieee754_rem_pio2 and __kernel_rem_pio2
> maybe slow is not the best name
> 
> the slow version is only called (by the non-slow one)
> for large numbers (>2^20) when multi precision arithmetic
> is needed using many bits of 2/pi
> (there is a large ipio2 array in that file)

Yes, that left me rather confused. The original names are confusing
too. In both cases it sounds like you have 2 versions of the same code
(fast/slow or different conformance profiles) rather than 2 different
functions. 

> maybe it should be called __rem_pio2_large ?

That's better. Not sure what the best name would be..

> > gcc-specific hacks involving __typeof__. If gcc4 allows _Generic even
> > with -std=c99 etc. then I think _Generic is the best.
> > 
> no, gcc does not support it
> (i have a gcc 4.7 built from svn in october and it has -std=c1x,
> but _Generic is not supported)
> 
> i wrote it using generic since i couldnt come up with
> any way to solve it in c99

There's no way to solve it in C99 matching the return types, but if
you don't care about return types, it's pretty easy (albeit ugly and
verbose) with ?: conditionals based on sizeof(x).

Since gcc still doesn't support _Generic, I think it might be
preferable to use the old approach (sizeof tests) and add (with a
conditionally defined macro) a cast to __typeof__((x)+1.0f) (or
__typeof__((x)+(y)+1.0f), etc. as needed) on the result that's only
enabled when __GNUC__ is defined. Then the fallback case would fail to
have the right result types, but at least the right functions would
get called.

> > > i wonder if it's ok to use a polyeval(coeffs, x) function instead
> > > of writing out the polynomial (some openbsd long double code uses
> > > such thing but not everywhere)
> > 
> > I suspect the functions that aren't using it right now depend on a
> > particular evaluation order to avoid loss of precision. I could be
> > wrong though; that's just my best guess.
> > 
> it's just a minor detail, but this was one of the parts where
> things could be a bit nicer and more consistent (probably)
> 
> (coeffs in array, and using some static inline eval etc,
> it might not be a good idea for some technical reason,
> but i dont yet see why)

If it's not going to change the behavior of the code and it's just a
matter of inlining/un-inlining, I'm fairly indifferent to how you want
to do it.

> > > todo:
> > > - move the code to musl tree so the changes are recorded there
> > 
> > Very interested in doing this sooner rather than later.
> > 
> ok
> complex is missing but it is not hard to add
> (if you want to wait for it)

Either way.

> the parts i wrote may need some review or testing
> (it can be done after merging of course)

Yes, I'm okay with that.

> (it would be nice if musl had a dedicated testing
> package where one could add test functions
> and easy to run)

There's the libc-testsuite git repo, and then cluts. Neither is
musl-specific. The former is much broader in scope; the latter is
probably more intensive in the few areas it tests.

> ok so using compound literal
> i was thinking about this
> something like this will work

OK.

> the difficult part is figuring out if it can be used internally
> 
> (many math functions start by turning the float/double
> into unsigned int, then doing various checks to handle
> special cases like nan, inf, signbit, etc using bithacks
> 
> i thought most of them could be isnan() etc
> eg compare the current fmax to the freebsd one
> 
> i'm not sure if this can be done in general
> because some bit hacks will be needed anyway
> and it might be better doing the conversion
> once

Yes, at least it would be a major undertaking to convert all the code
and ensure that no bugs are introduced. I think it's fine leaving it
how it is for now. I still want the fast isnan, etc. for applications
to use, though (and for use by other parts of libc like printf).

> many functions do checks like
>   if(x < 12.34) ... else if(x < 78.9) ...
> but using unsigned int arithmetics like
>   if ((x&bitmask) < 0xabcdef) ...
> which is not clear, but i guess this
> cannot be changed because float compare
> does a different thing
> but it would be nice to reduce the bithacks
> without changing the semantics or performance)

The semantics should only be changed if the goal is to consider
NAN/INF as "large" values too. Of course you also get abs() for free
using the representation and bitmask. Otherwise, for finite
comparisons comparing the representations and comparing the
floating-point values should have the same behavior.

Rich


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-04 18:43             ` libm Rich Felker
@ 2012-03-05  8:51               ` Szabolcs Nagy
  2012-03-05 14:04                 ` libm Rich Felker
  0 siblings, 1 reply; 28+ messages in thread
From: Szabolcs Nagy @ 2012-03-05  8:51 UTC (permalink / raw)
  To: musl

* Rich Felker <dalias@aerifal.cx> [2012-03-04 13:43:39 -0500]:
> On Sun, Mar 04, 2012 at 03:50:41PM +0100, Szabolcs Nagy wrote:
> > maybe it should be called __rem_pio2_large ?
> 
> That's better. Not sure what the best name would be..
> 
renamed

> > i wrote it using generic since i couldnt come up with
> > any way to solve it in c99
> 
> There's no way to solve it in C99 matching the return types, but if
> you don't care about return types, it's pretty easy (albeit ugly and
> verbose) with ?: conditionals based on sizeof(x).
> 
hm i haven't even thought about return types

my problem was calling the double version of a function when the
arg is integral and the float version when the arg is float.
(it can be done with sizeof if we can assume there is a larger
integer type than float, but i thought that was ugly)

even if we don't care about return types and int arguments
it seems tgmath is very ugly

> > complex is missing but it is not hard to add
> > (if you want to wait for it)
> 
> Either way.
> 
> > the parts i wrote may need some review or testing
> > (it can be done after merging of course)
> 
> Yes, I'm okay with that.
> 

i can do the merge and then you git pull
or you can do it from libm

the long double ifdefs needs to be fixed
and math.h sorted out

> There's the libc-testsuite git repo, and then cluts. Neither is
> musl-specific. The former is much broader in scope; the latter is
> probably more intensive in the few areas it tests.
> 

i know but i'm not content with either of those
imho there should be one libctest repo which is a bit more
organized than libc-testsuit and contains the cluts tests
as well

> > ok so using compound literal
> > i was thinking about this
> > something like this will work
> 
> OK.
> 

i saw that you removed the compound literal definition of
NAN, INFINITY etc from math.h

if we want to keep the headers c89 friendly then
isnan, isinf,.. cannot be defined without function call
maybe use static __isnanf,.. functions?

(i committed a compound literal based implementatoin for now)

> Yes, at least it would be a major undertaking to convert all the code
> and ensure that no bugs are introduced. I think it's fine leaving it
> how it is for now. I still want the fast isnan, etc. for applications
> to use, though (and for use by other parts of libc like printf).
> 

i agree

> The semantics should only be changed if the goal is to consider
> NAN/INF as "large" values too. Of course you also get abs() for free
> using the representation and bitmask. Otherwise, for finite
> comparisons comparing the representations and comparing the
> floating-point values should have the same behavior.
> 
hm but float compare will compile to different instruction
so there is at least performance difference


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-04 14:50           ` libm Szabolcs Nagy
  2012-03-04 18:43             ` libm Rich Felker
@ 2012-03-05 11:08             ` Szabolcs Nagy
  1 sibling, 0 replies; 28+ messages in thread
From: Szabolcs Nagy @ 2012-03-05 11:08 UTC (permalink / raw)
  To: musl

* Szabolcs Nagy <nsz@port70.net> [2012-03-04 15:50:41 +0100]:
> * Rich Felker <dalias@aerifal.cx> [2012-03-04 01:53:40 -0500]:
> > On Sat, Mar 03, 2012 at 11:57:58PM +0100, Szabolcs Nagy wrote:
> > > how to do the long double ifdefs?
> > 
> > #if LDBL_MANT_DIG == ...
> > 
> ok will do that
> 

i use the following checks in all long double code now
LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384

the inplace union declarations for bit hacks are not ok
anymore because endianness is not checked

the union declarations should go to an arch specific .h
eg. arch/i386/longdouble.h

but before that long double bit hacks may need some
cleanups (freebsd union vs openbsd macros vs ..)


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-05  8:51               ` libm Szabolcs Nagy
@ 2012-03-05 14:04                 ` Rich Felker
  2012-03-05 15:17                   ` libm Szabolcs Nagy
  0 siblings, 1 reply; 28+ messages in thread
From: Rich Felker @ 2012-03-05 14:04 UTC (permalink / raw)
  To: musl

On Mon, Mar 05, 2012 at 09:51:35AM +0100, Szabolcs Nagy wrote:
> > > i wrote it using generic since i couldnt come up with
> > > any way to solve it in c99
> > 
> > There's no way to solve it in C99 matching the return types, but if
> > you don't care about return types, it's pretty easy (albeit ugly and
> > verbose) with ?: conditionals based on sizeof(x).
> > 
> hm i haven't even thought about return types
> 
> my problem was calling the double version of a function when the
> arg is integral and the float version when the arg is float.
> (it can be done with sizeof if we can assume there is a larger
> integer type than float, but i thought that was ugly)

Hm? If you need a macro to test whether an argument is an integer or
floating point, try this:

#define __IS_FP(x) !!((1?1:(x))/2)

No assumptions at all.

> even if we don't care about return types and int arguments
> it seems tgmath is very ugly

Yes, it is ugly. I don't think there's any way around the fact that
tgmath.h is ugly.

> > > complex is missing but it is not hard to add
> > > (if you want to wait for it)
> > 
> > Either way.
> > 
> > > the parts i wrote may need some review or testing
> > > (it can be done after merging of course)
> > 
> > Yes, I'm okay with that.
> > 
> 
> i can do the merge and then you git pull
> or you can do it from libm
> 
> the long double ifdefs needs to be fixed
> and math.h sorted out

I can do it, but might be another day or two. Feel free to keep fixing
stuff up in the mean time if you have time. :)

> > There's the libc-testsuite git repo, and then cluts. Neither is
> > musl-specific. The former is much broader in scope; the latter is
> > probably more intensive in the few areas it tests.
> > 
> 
> i know but i'm not content with either of those
> imho there should be one libctest repo which is a bit more
> organized than libc-testsuit and contains the cluts tests
> as well
> 
> > > ok so using compound literal
> > > i was thinking about this
> > > something like this will work
> > 
> > OK.
> > 
> 
> i saw that you removed the compound literal definition of
> NAN, INFINITY etc from math.h
> 
> if we want to keep the headers c89 friendly then
> isnan, isinf,.. cannot be defined without function call
> maybe use static __isnanf,.. functions?

The removal has nothing to do with c89; it's actually the fact that
the compound literal definition was not a constant expression and the
standard requires these macros to expand to constant expressions.

As far as I know, gcc allows the compound literal even in old-standard
modes, probably because it was a feature of "GNU C" long before C99
adopted it. In any case I see no good way to define them without
compound literals except the function calls..

> > The semantics should only be changed if the goal is to consider
> > NAN/INF as "large" values too. Of course you also get abs() for free
> > using the representation and bitmask. Otherwise, for finite
> > comparisons comparing the representations and comparing the
> > floating-point values should have the same behavior.
> > 
> hm but float compare will compile to different instruction
> so there is at least performance difference

Yes. In principle it would be faster to do everything as floating
point so no store/load delays are needed. In relality I doubt it makes
much of a difference. In almost all of these functions the actual
computation time dominates.

Rich


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-05 14:04                 ` libm Rich Felker
@ 2012-03-05 15:17                   ` Szabolcs Nagy
  2012-03-05 15:25                     ` libm Rich Felker
  2012-03-09 10:22                     ` libm Rich Felker
  0 siblings, 2 replies; 28+ messages in thread
From: Szabolcs Nagy @ 2012-03-05 15:17 UTC (permalink / raw)
  To: musl

* Rich Felker <dalias@aerifal.cx> [2012-03-05 09:04:59 -0500]:
> On Mon, Mar 05, 2012 at 09:51:35AM +0100, Szabolcs Nagy wrote:
> Hm? If you need a macro to test whether an argument is an integer or
> floating point, try this:
> 
> #define __IS_FP(x) !!((1?1:(x))/2)
> 

oh nice
(and subtle)

> I can do it, but might be another day or two. Feel free to keep fixing
> stuff up in the mean time if you have time. :)
> 

ok

> > i saw that you removed the compound literal definition of
> > NAN, INFINITY etc from math.h
> > 
> 
> The removal has nothing to do with c89; it's actually the fact that
> the compound literal definition was not a constant expression and the
> standard requires these macros to expand to constant expressions.
> 

i see

> adopted it. In any case I see no good way to define them without
> compound literals except the function calls..
> 

btw while testing these macros i noticed that when
multiple classification macros are used fpclassify
gets called many times
(with the previous solution)

the extra calls could be optimized by adding
__attribute__((const)) to fpclassify
(resulted less calls, smaller code size etc)

> > hm but float compare will compile to different instruction
> > so there is at least performance difference
> 
> Yes. In principle it would be faster to do everything as floating
> point so no store/load delays are needed. In relality I doubt it makes
> much of a difference. In almost all of these functions the actual
> computation time dominates.
> 

i see


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-05 15:17                   ` libm Szabolcs Nagy
@ 2012-03-05 15:25                     ` Rich Felker
  2012-03-09 10:22                     ` libm Rich Felker
  1 sibling, 0 replies; 28+ messages in thread
From: Rich Felker @ 2012-03-05 15:25 UTC (permalink / raw)
  To: musl

On Mon, Mar 05, 2012 at 04:17:11PM +0100, Szabolcs Nagy wrote:
> * Rich Felker <dalias@aerifal.cx> [2012-03-05 09:04:59 -0500]:
> > On Mon, Mar 05, 2012 at 09:51:35AM +0100, Szabolcs Nagy wrote:
> > Hm? If you need a macro to test whether an argument is an integer or
> > floating point, try this:
> > 
> > #define __IS_FP(x) !!((1?1:(x))/2)
> 
> oh nice
> (and subtle)

Note that it avoids evaluating x too, so it's safe in macros that need
to avoid evaluating the argument more than once.

> > adopted it. In any case I see no good way to define them without
> > compound literals except the function calls..
> > 
> 
> btw while testing these macros i noticed that when
> multiple classification macros are used fpclassify
> gets called many times
> (with the previous solution)
> 
> the extra calls could be optimized by adding
> __attribute__((const)) to fpclassify
> (resulted less calls, smaller code size etc)

Indeed. I was wrongly thinking they'd need __attribute__((__pure__))
rather than const, which is trickier because it's only available in
newish gcc. But const has been around since before 2.95 even, so just
#ifdef __GNUC__ is a sufficient check for using it.

Alternatively I might just move the definitions of fpclassify (at
least for non-LD types) back into math.h as static inline. This would
let gcc optimize it down a lot more.

Rich


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-05 15:17                   ` libm Szabolcs Nagy
  2012-03-05 15:25                     ` libm Rich Felker
@ 2012-03-09 10:22                     ` Rich Felker
  2012-03-09 10:57                       ` libm Szabolcs Nagy
  2012-03-09 11:09                       ` libm Szabolcs Nagy
  1 sibling, 2 replies; 28+ messages in thread
From: Rich Felker @ 2012-03-09 10:22 UTC (permalink / raw)
  To: musl

On Mon, Mar 05, 2012 at 04:17:11PM +0100, Szabolcs Nagy wrote:
> * Rich Felker <dalias@aerifal.cx> [2012-03-05 09:04:59 -0500]:
> > On Mon, Mar 05, 2012 at 09:51:35AM +0100, Szabolcs Nagy wrote:
> > Hm? If you need a macro to test whether an argument is an integer or
> > floating point, try this:
> > 
> > #define __IS_FP(x) !!((1?1:(x))/2)
> > 
> 
> oh nice
> (and subtle)

I checked out your latest tgmath.h; it's surprisingly clean and
readable! A couple ideas..

1. __fun could be just fun (macro arguments are free of namespace
issues)

2. You could add something like:
#ifdef __GNUC__
#define __RETCAST(x) (__typeof__((x))
#else
#define __RETCAST(x)
#endif
Then add __RETCAST((x)), __RETCAST((x)+(y)), etc. Some trick will be
needed to make integer types result in a cast to double, though.

Rich


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-09 10:22                     ` libm Rich Felker
@ 2012-03-09 10:57                       ` Szabolcs Nagy
  2012-03-09 16:01                         ` libm Rich Felker
  2012-03-09 11:09                       ` libm Szabolcs Nagy
  1 sibling, 1 reply; 28+ messages in thread
From: Szabolcs Nagy @ 2012-03-09 10:57 UTC (permalink / raw)
  To: musl

* Rich Felker <dalias@aerifal.cx> [2012-03-09 05:22:39 -0500]:

> On Mon, Mar 05, 2012 at 04:17:11PM +0100, Szabolcs Nagy wrote:
> > * Rich Felker <dalias@aerifal.cx> [2012-03-05 09:04:59 -0500]:
> > > On Mon, Mar 05, 2012 at 09:51:35AM +0100, Szabolcs Nagy wrote:
> > > Hm? If you need a macro to test whether an argument is an integer or
> > > floating point, try this:
> > > 
> > > #define __IS_FP(x) !!((1?1:(x))/2)
> > > 
> > 
> > oh nice
> > (and subtle)
> 
> I checked out your latest tgmath.h; it's surprisingly clean and
> readable! A couple ideas..
> 
> 1. __fun could be just fun (macro arguments are free of namespace
> issues)
> 

hm true

> 2. You could add something like:
> #ifdef __GNUC__
> #define __RETCAST(x) (__typeof__((x))
> #else
> #define __RETCAST(x)
> #endif
> Then add __RETCAST((x)), __RETCAST((x)+(y)), etc. Some trick will be
> needed to make integer types result in a cast to double, though.
> 

ok, that makes sense

actually i'm not sure when the extra () protection
is needed like __typeof__((x))
(the reason for it in (x)+(y) is clear)

is it because single argument macros might get
called with a comma expression?

so
#define A(x) B((x))
is ok

but
#define A(x,y) B((x),(y))
is redundant


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-09 10:22                     ` libm Rich Felker
  2012-03-09 10:57                       ` libm Szabolcs Nagy
@ 2012-03-09 11:09                       ` Szabolcs Nagy
  2012-03-09 15:56                         ` libm Rich Felker
  1 sibling, 1 reply; 28+ messages in thread
From: Szabolcs Nagy @ 2012-03-09 11:09 UTC (permalink / raw)
  To: musl

* Rich Felker <dalias@aerifal.cx> [2012-03-09 05:22:39 -0500]:
> 2. You could add something like:
> #ifdef __GNUC__
> #define __RETCAST(x) (__typeof__((x))
> #else
> #define __RETCAST(x)
> #endif
> Then add __RETCAST((x)), __RETCAST((x)+(y)), etc. Some trick will be
> needed to make integer types result in a cast to double, though.

hm the int->double and complex/real cases are tricky

i thought +1.0 or +I would solve these, but that's wrong


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-09 11:09                       ` libm Szabolcs Nagy
@ 2012-03-09 15:56                         ` Rich Felker
  2012-03-09 17:02                           ` libm Rich Felker
  0 siblings, 1 reply; 28+ messages in thread
From: Rich Felker @ 2012-03-09 15:56 UTC (permalink / raw)
  To: musl

On Fri, Mar 09, 2012 at 12:09:46PM +0100, Szabolcs Nagy wrote:
> * Rich Felker <dalias@aerifal.cx> [2012-03-09 05:22:39 -0500]:
> > 2. You could add something like:
> > #ifdef __GNUC__
> > #define __RETCAST(x) (__typeof__((x))
> > #else
> > #define __RETCAST(x)
> > #endif
> > Then add __RETCAST((x)), __RETCAST((x)+(y)), etc. Some trick will be
> > needed to make integer types result in a cast to double, though.
> 
> hm the int->double and complex/real cases are tricky
> 
> i thought +1.0 or +I would solve these, but that's wrong

I think +0.0f might solve it. Isn't the promoted type of any integer
type with float double?

Rich


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-09 10:57                       ` libm Szabolcs Nagy
@ 2012-03-09 16:01                         ` Rich Felker
  0 siblings, 0 replies; 28+ messages in thread
From: Rich Felker @ 2012-03-09 16:01 UTC (permalink / raw)
  To: musl

On Fri, Mar 09, 2012 at 11:57:03AM +0100, Szabolcs Nagy wrote:
> > 2. You could add something like:
> > #ifdef __GNUC__
> > #define __RETCAST(x) (__typeof__((x))
> > #else
> > #define __RETCAST(x)
> > #endif
> > Then add __RETCAST((x)), __RETCAST((x)+(y)), etc. Some trick will be
> > needed to make integer types result in a cast to double, though.
> > 
> 
> ok, that makes sense
> 
> actually i'm not sure when the extra () protection
> is needed like __typeof__((x))
> (the reason for it in (x)+(y) is clear)
> 
> is it because single argument macros might get
> called with a comma expression?
> 
> so
> #define A(x) B((x))
> is ok
> 
> but
> #define A(x,y) B((x),(y))
> is redundant

It's probably redundant. Only case I can think of where it might help
is improving(?) the error reporting when some invalid-as-macro-arg
expressions that use ?: as a grouping for a comma operator in the
second operand, or perhaps some fishy stuff with compound literals.

Rich


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-09 15:56                         ` libm Rich Felker
@ 2012-03-09 17:02                           ` Rich Felker
  2012-03-10  3:28                             ` libm Rich Felker
  0 siblings, 1 reply; 28+ messages in thread
From: Rich Felker @ 2012-03-09 17:02 UTC (permalink / raw)
  To: musl

On Fri, Mar 09, 2012 at 10:56:55AM -0500, Rich Felker wrote:
> > > Then add __RETCAST((x)), __RETCAST((x)+(y)), etc. Some trick will be
> > > needed to make integer types result in a cast to double, though.
> > 
> > hm the int->double and complex/real cases are tricky
> > 
> > i thought +1.0 or +I would solve these, but that's wrong
> 
> I think +0.0f might solve it. Isn't the promoted type of any integer
> type with float double?

Nope, it results in float. glibc has a trick involving obscure
interactions of null pointer constants and the ?: operator that
generates the right type using __typeof__, which is no big deal
because this code is already only used when __typeof__ is available,
anyway.

There's a good blog article on it somewhere which I can't seem to find
at the moment...

Rich


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-09 17:02                           ` libm Rich Felker
@ 2012-03-10  3:28                             ` Rich Felker
  2012-03-10 12:45                               ` libm Szabolcs Nagy
  0 siblings, 1 reply; 28+ messages in thread
From: Rich Felker @ 2012-03-10  3:28 UTC (permalink / raw)
  To: musl

On Fri, Mar 09, 2012 at 12:02:54PM -0500, Rich Felker wrote:
> On Fri, Mar 09, 2012 at 10:56:55AM -0500, Rich Felker wrote:
> > > > Then add __RETCAST((x)), __RETCAST((x)+(y)), etc. Some trick will be
> > > > needed to make integer types result in a cast to double, though.
> > > 
> > > hm the int->double and complex/real cases are tricky
> > > 
> > > i thought +1.0 or +I would solve these, but that's wrong
> > 
> > I think +0.0f might solve it. Isn't the promoted type of any integer
> > type with float double?
> 
> Nope, it results in float. glibc has a trick involving obscure
> interactions of null pointer constants and the ?: operator that
> generates the right type using __typeof__, which is no big deal
> because this code is already only used when __typeof__ is available,
> anyway.
> 
> There's a good blog article on it somewhere which I can't seem to find
> at the moment...

Try this:

#define __RETCAST(x) (__typeof__(*(0 \
? (__typeof__(0 ? (double *)0 : (void *)__IS_FP(x)))0 \
: (__typeof__(0 ? (__typeof__(x) *)0 : (void *)!__IS_FP(x)))0 )))

In the outer conditional operator, the second operand is a pointer to
double if (void *)__IS_FP(x) is a null pointer constant (which is true
iff __IS_FP(x)==0) and otherwise is a pointer to void.

Conversely, the third operand of the outer conditional is a pointer to
__typeof__(x) iff __IS_FP(x)!=0, and otherwise a pointer to void.

Thus the outer conditional sees either "double *" and "void *", or
else "void *" and "__typeof__(x) *", resulting it in having type
"double *" or "__typeof__(x)", reflecting whether x was an integer or
floating point type.

Rich


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-10  3:28                             ` libm Rich Felker
@ 2012-03-10 12:45                               ` Szabolcs Nagy
  2012-03-10 13:12                                 ` libm Rich Felker
  0 siblings, 1 reply; 28+ messages in thread
From: Szabolcs Nagy @ 2012-03-10 12:45 UTC (permalink / raw)
  To: musl

* Rich Felker <dalias@aerifal.cx> [2012-03-09 22:28:44 -0500]:
> Try this:
> 
> #define __RETCAST(x) (__typeof__(*(0 \
> ? (__typeof__(0 ? (double *)0 : (void *)__IS_FP(x)))0 \
> : (__typeof__(0 ? (__typeof__(x) *)0 : (void *)!__IS_FP(x)))0 )))
> 
> In the outer conditional operator, the second operand is a pointer to
> double if (void *)__IS_FP(x) is a null pointer constant (which is true
> iff __IS_FP(x)==0) and otherwise is a pointer to void.
> 
> Conversely, the third operand of the outer conditional is a pointer to
> __typeof__(x) iff __IS_FP(x)!=0, and otherwise a pointer to void.
> 
> Thus the outer conditional sees either "double *" and "void *", or
> else "void *" and "__typeof__(x) *", resulting it in having type
> "double *" or "__typeof__(x)", reflecting whether x was an integer or
> floating point type.
> 

of course there was another twist:
some complex functions return real value :)

i solved that with a __TO_REAL(x) macro using similar tricks
so __RETCAST(__TO_REAL(x)) gives correct return type


there are two remaining problems:

for two argument functions __RETCAST((x)+(y)) is not ok
if x is float, y is int then the return type should be double, not float
(this can be solved by a __RETCAST2(x,y) i guess)

if sizeof(long double) == sizeof(double) then the selected
function will be wrong
(it should not matter much, we can leave it as is or enforce double)


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-10 12:45                               ` libm Szabolcs Nagy
@ 2012-03-10 13:12                                 ` Rich Felker
  2012-03-10 16:38                                   ` libm Szabolcs Nagy
  0 siblings, 1 reply; 28+ messages in thread
From: Rich Felker @ 2012-03-10 13:12 UTC (permalink / raw)
  To: musl

On Sat, Mar 10, 2012 at 01:45:53PM +0100, Szabolcs Nagy wrote:
> there are two remaining problems:
> 
> for two argument functions __RETCAST((x)+(y)) is not ok
> if x is float, y is int then the return type should be double, not float
> (this can be solved by a __RETCAST2(x,y) i guess)

I don't see any better way right off...
Actually it's really ugly that C's promotions don't respect the size
of the integer type when combining with floating point types. x+0.0f
where x is an integer type is almost sure to destroy the value of x
except for very small values. C really should have made the promotion
be to the smallest floating point type that has at least as many bits
in its mantissa as value bits in the integer type, or long double if
no such type exists.

Or from the other side (with C as it is), programmers should be really
careful about implicit conversions to floating point types from
integer types.

> if sizeof(long double) == sizeof(double) then the selected
> function will be wrong
> (it should not matter much, we can leave it as is or enforce double)

It would probably be nice to put the case for double before long
double to avoid going through an extra wrapper function on LD64
systems, but it's not a big deal.

Rich


^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: libm
  2012-03-10 13:12                                 ` libm Rich Felker
@ 2012-03-10 16:38                                   ` Szabolcs Nagy
  0 siblings, 0 replies; 28+ messages in thread
From: Szabolcs Nagy @ 2012-03-10 16:38 UTC (permalink / raw)
  To: musl

* Rich Felker <dalias@aerifal.cx> [2012-03-10 08:12:45 -0500]:

> On Sat, Mar 10, 2012 at 01:45:53PM +0100, Szabolcs Nagy wrote:
> > for two argument functions __RETCAST((x)+(y)) is not ok
> > if x is float, y is int then the return type should be double, not float
> > (this can be solved by a __RETCAST2(x,y) i guess)
> 
> I don't see any better way right off...

i committed a solution
handling all the combinations turned out to be quite tricky


> Actually it's really ugly that C's promotions don't respect the size
> of the integer type when combining with floating point types. x+0.0f

i'd say that the ugly thing is that they added tgmath.h
eventhough there is no good way to do type generics in c

the implicit type conversions and promotions are already
hard to tackle in c so one should be careful anyway
(and use explicit casts when not sure)

> It would probably be nice to put the case for double before long
> double to avoid going through an extra wrapper function on LD64
> systems, but it's not a big deal.

i made the long double conditions false whenever
sizeof(double) == sizeof(long double)
so on LD64 only double functions will be used


^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2012-03-10 16:38 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-01-23 16:41 libm Szabolcs Nagy
2012-01-23 17:07 ` libm Rich Felker
2012-01-23 19:12   ` libm Szabolcs Nagy
2012-01-27 16:02     ` libm Szabolcs Nagy
2012-01-27 19:01       ` libm Pascal Cuoq
2012-01-27 19:34         ` libm Rich Felker
2012-01-29 16:34           ` libm Szabolcs Nagy
2012-02-27 21:02   ` libm Szabolcs Nagy
2012-02-27 22:24     ` libm Rich Felker
2012-03-03 22:57       ` libm Szabolcs Nagy
2012-03-04  6:53         ` libm Rich Felker
2012-03-04 14:50           ` libm Szabolcs Nagy
2012-03-04 18:43             ` libm Rich Felker
2012-03-05  8:51               ` libm Szabolcs Nagy
2012-03-05 14:04                 ` libm Rich Felker
2012-03-05 15:17                   ` libm Szabolcs Nagy
2012-03-05 15:25                     ` libm Rich Felker
2012-03-09 10:22                     ` libm Rich Felker
2012-03-09 10:57                       ` libm Szabolcs Nagy
2012-03-09 16:01                         ` libm Rich Felker
2012-03-09 11:09                       ` libm Szabolcs Nagy
2012-03-09 15:56                         ` libm Rich Felker
2012-03-09 17:02                           ` libm Rich Felker
2012-03-10  3:28                             ` libm Rich Felker
2012-03-10 12:45                               ` libm Szabolcs Nagy
2012-03-10 13:12                                 ` libm Rich Felker
2012-03-10 16:38                                   ` libm Szabolcs Nagy
2012-03-05 11:08             ` libm Szabolcs Nagy

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/musl/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).