From: "Oliver Schädlich" <oliver.schaedlich@gmail.com>
To: libc-help@sourceware.org
Subject: More efficient fmod()
Date: Sat, 1 Oct 2022 18:34:16 +0200 [thread overview]
Message-ID: <211e8b64-519a-6037-62fc-7fcac1983ac4@gmail.com> (raw)
I found that fmod() could be faster.
This is my implementation:
#include <stdint.h>
#include <string.h>
#include <fenv.h>
#if defined(_MSC_VER)
#include <intrin.h>
#endif
#if defined(__GNUC__) || defined(__clang__)
#define likely(x) __builtin_expect((x), 1)
#define unlikely(x) __builtin_expect((x), 0)
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif
inline uint64_t bin( double d )
{
uint64_t u;
memcpy( &u, &d, sizeof d );
return u;
}
inline double dbl( uint64_t u )
{
double d;
memcpy( &d, &u, sizeof u );
return d;
}
inline double invalid( uint64_t u )
{
feraiseexcept( FE_INVALID );
return dbl( u );
}
#define SIGN_BIT ((uint64_t)1 << 63)
#define EXP_MASK ((uint64_t)0x7FF << 52)
#define IMPLCIT_BIT ((uint64_t)1 << 52)
#define MANT_MASK (IMPLCIT_BIT - 1)
#define QNAN_BIT (IMPLCIT_BIT >> 1)
inline void normalize( uint64_t *mant, int *exp )
{
#if defined(__GNUC__) || defined(__clang__)
unsigned bits;
bits = __builtin_clz( *mant ) - 11;
*mant <<= bits;
#elif defined(_MSC_VER)
unsigned long bits;
_BitScanReverse64( &bits, *mant );
*mant <<= bits - 11 ;
#else
unsigned bits;
for( bits = 0; !(*mant & IMPLCIT_BIT); *mant <<= 1, ++bits );
#endif
*exp -= bits;
}
double myFmodC( double counter, double denominator )
{
uint64_t const
bCounter = bin( counter ),
bDenom = bin( denominator );
uint64_t const sign = (bCounter ^ bDenom) & SIGN_BIT;
if( unlikely((bCounter & EXP_MASK) == EXP_MASK) )
// +/-[Inf|QNaN|SNaN] % ... = -QNaN
// follow SSE/AVX-rules, first NaN rules, i.e.
// first parameter determines non-SNaN/QNaN-bits
return invalid( SIGN_BIT | bCounter | QNAN_BIT );
if( unlikely((bDenom & EXP_MASK) == EXP_MASK) )
// +/-x % +/-[Inf|QNan|SNaN]
if( likely(!(bDenom & MANT_MASK)) )
// +/-x % +/-Inf = -/+x
return dbl( sign | bCounter & ~SIGN_BIT );
else
// +/-x % +/-[QNaN|SNaN] = -NaN
return invalid( SIGN_BIT | bDenom | QNAN_BIT );
int
counterExp = (bCounter & EXP_MASK) >> 52,
denomExp = (bDenom & EXP_MASK) >> 52;
uint64_t
counterMant = (uint64_t)!!counterExp << 52 | bCounter & MANT_MASK,
denomMant = (uint64_t)!!denomExp << 52 | bDenom & MANT_MASK;
if( unlikely(!counterExp) )
// counter is denormal
if( likely(!counterMant) )
// counter == +/-0.0
if( likely(denomMant) )
// +/-0.0 % +/-x = -/+0.0
return dbl( sign );
else
// +/-0.0 % +/-0.0 = -QNaN
return invalid( SIGN_BIT | EXP_MASK | QNAN_BIT );
else
// normalize counter
normalize( &counterMant, &counterExp ),
++counterExp;
if( unlikely(!denomExp) )
// denominator is denormal
if( likely(!denomMant) )
// +/-x % +/-0.0 = -/+QNaN
return invalid( SIGN_BIT | EXP_MASK | QNAN_BIT );
else
// normalize denominator
normalize( &denomMant, &denomExp ),
++denomExp;
int exp = counterExp;
uint64_t remainderMant = counterMant;
for( ; ; )
{
int below = remainderMant < denomMant;
if( unlikely(exp - below < denomExp) )
break;
exp -= below;
remainderMant <<= below;
if( unlikely(!(remainderMant -= denomMant)) )
{
exp = 0;
break;
}
normalize( &remainderMant, &exp );
};
if( unlikely(exp <= 0) )
// denormal result
remainderMant >>= -exp + 1,
exp = 0;
return dbl( sign | (uint64_t)exp << 52 | remainderMant & MANT_MASK );
}
If I chose random pairs of doubles the above code takes nearly
40% less time on my Zen1-CPU than with the current glibc-Code.
next reply other threads:[~2022-10-01 16:34 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-10-01 16:34 Oliver Schädlich [this message]
2022-10-01 20:19 ` Adhemerval Zanella Netto
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=211e8b64-519a-6037-62fc-7fcac1983ac4@gmail.com \
--to=oliver.schaedlich@gmail.com \
--cc=libc-help@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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 read-only IMAP folder(s) and NNTP newsgroup(s).