/* IMPLEMENTATION OPTIONS. Can be set to either 0 or 1, whichever seems
to be fastest. */
#define USE_SIMD 1/* Use SIMD intrinsics (SSE2/AVX) if available? */
#define USE_MEMSET_SMALL \ 1/* Use memset rather than a loop (for small mem)? \
*/ #define USE_MEMSET_LARGE \ 1/* Use memset rather than a loop (for large mem)? \
*/ #define USE_USED_LARGE 1/* Use the used flags in a large accumulator? */
#define OPT_SMALL 0/* Class of manual optimization for operations on */ /* small accumulator: 0 (none), 1, 2, 3 (SIMD) */ #define OPT_CARRY 1/* Use manually optimized carry propagation? */
#define OPT_LARGE_SUM 1/* Should manually optimized routines be used for */ #define OPT_LARGE_SQNORM 1/* operations using the large accumulator? */ #define OPT_LARGE_DOT 1
#define OPT_SIMPLE_SUM 1/* Should manually optimized routines be used for */ #define OPT_SIMPLE_SQNORM 1/* operations done with simple FP arithmetic? */ #define OPT_SIMPLE_DOT 1
#define OPT_KAHAN_SUM 0/* Use manually optimized routine for Kahan sum? */
#define INLINE_SMALL 1/* Inline more of the small accumulator routines? */ /* (Not currently used) */ #define INLINE_LARGE 1/* Inline more of the large accumulator routines? */
/* INCLUDE INTEL INTRINSICS IF USED AND AVAILABLE. */
#if USE_SIMD && __SSE2__ # include <immintrin.h> #endif
/* COPY A 64-BIT QUANTITY - DOUBLE TO 64-BIT INT OR VICE VERSA. The
arguments are destination and source variables (not values). */
/* ADD AN INF OR NAN TO A SMALL ACCUMULATOR. This only changes the flags, notthechunksintheaccumulator,whichretainsthesumofthefinite terms(whichisperhapssometimesusefultoaccess,thoughnofunction todosoisdefinedatpresent).ANaNwithlargerpayload(seenasa 52-bitunsignedinteger)takesprecedence,withthesignoftheNaNalways beingpositive.ThisensuresthattheorderofsummingNaNvaluesdoesn't
matter. */
if (mantissa == 0) /* Inf */
{ if (sacc->Inf == 0) { /* no previous Inf */
sacc->Inf = ivalue;
} elseif (sacc->Inf != ivalue) { /* previous Inf was opposite sign */
COPY64(fltv, ivalue);
fltv = fltv - fltv; /* result will be a NaN */
COPY64(sacc->Inf, fltv);
}
} else/* NaN */
{ /* Choose the NaN with the bigger payload and clear its sign. Using <=
ensures that we will choose the first NaN over the previous zero. */ if ((sacc->NaN & XSUM_MANTISSA_MASK) <= mantissa) {
sacc->NaN = ivalue & ~XSUM_SIGN_MASK;
}
}
}
/* PROPAGATE CARRIES TO NEXT CHUNK IN A SMALL ACCUMULATOR. Needs to becalledoftenenoughthataccumulatedcarriesdon'toverflowout thetop,asindicatedbysacc->adds_until_propagate.Returnsthe indexoftheuppermostnon-zerochunk(0ifnumberiszero).
static NOINLINE int xsum_carry_propagate(xsum_small_accumulator* sacc) { int i, u, uix;
if (xsum_debug) printf("\nCARRY PROPAGATING IN SMALL ACCUMULATOR\n");
/* Set u to the index of the uppermost non-zero (for now) chunk, or
return with value 0 if there is none. */
#if OPT_CARRY
{
u = XSUM_SCHUNKS - 1; switch (XSUM_SCHUNKS & 0x3) /* get u to be a multiple of 4 minus one */
{ case3: if (sacc->chunk[u] != 0) { goto found2;
}
u -= 1; /* XSUM_SCHUNKS is a */ case2: if (sacc->chunk[u] != 0) /* constant, so the */
{ goto found2; /* compiler will do */
} /* simple code here */
u -= 1; case1: if (sacc->chunk[u] != 0) { goto found2;
}
u -= 1; case0:;
}
do/* here, u should be a multiple of 4 minus one, and at least 3 */
{ # if USE_SIMD && __AVX__
{
__m256i ch;
ch = _mm256_loadu_si256((__m256i*)(sacc->chunk + u - 3)); if (!_mm256_testz_si256(ch, ch)) { goto found;
}
u -= 4; if (u < 0) /* never actually happens, because value of XSUM_SCHUNKS */
{ break; /* is such that u < 0 occurs at end of do loop instead */
}
ch = _mm256_loadu_si256((__m256i*)(sacc->chunk + u - 3)); if (!_mm256_testz_si256(ch, ch)) { goto found;
}
u -= 4;
} # else
{ if (sacc->chunk[u] | sacc->chunk[u - 1] | sacc->chunk[u - 2] |
sacc->chunk[u - 3]) { goto found;
}
u -= 4;
} # endif
} while (u >= 0);
if (xsum_debug) printf("number is zero (1)\n");
uix = 0; goto done;
found: if (sacc->chunk[u] != 0) { goto found2;
}
u -= 1; if (sacc->chunk[u] != 0) { goto found2;
}
u -= 1; if (sacc->chunk[u] != 0) { goto found2;
}
u -= 1;
found2:;
}
#else/* Non-optimized search for uppermost non-zero chunk */
{ for (u = XSUM_SCHUNKS - 1; sacc->chunk[u] == 0; u--) { if (u == 0) { if (xsum_debug) printf("number is zero (1)\n");
uix = 0; goto done;
}
}
}
#endif
/* At this point, sacc->chunk[u] must be non-zero */
if (xsum_debug) printf("u: %d, sacc->chunk[u]: %ld", u, sacc->chunk[u]);
/* Carry propagate, starting at the low-order chunks. Note that the
loop limit of u may be increased inside the loop. */
i = 0; /* set to the index of the next non-zero chunck, from bottom */
#if OPT_CARRY
{ /* Quickly skip over unused low-order chunks. Done here at the start onthetheorythatthereareoftenmanyunusedlow-orderchunks, justifyingsomeoverheadtobegin,butlaterstretchesofunused
chunks may not be as large. */
int e = u - 3; /* go only to 3 before so won't access beyond chunk array */
do { # if USE_SIMD && __AVX__
{
__m256i ch;
ch = _mm256_loadu_si256((__m256i*)(sacc->chunk + i)); if (!_mm256_testz_si256(ch, ch)) { break;
}
i += 4; if (i >= e) { break;
}
ch = _mm256_loadu_si256((__m256i*)(sacc->chunk + i)); if (!_mm256_testz_si256(ch, ch)) { break;
}
} # else
{ if (sacc->chunk[i] | sacc->chunk[i + 1] | sacc->chunk[i + 2] |
sacc->chunk[i + 3]) { break;
}
} # endif
i += 4;
} while (i <= e);
} #endif
uix = -1; /* indicates that a non-zero chunk has not been found yet */
do {
xsum_schunk c; /* Set to the chunk at index i (next non-zero one) */
xsum_schunk clow; /* Low-order bits of c */
xsum_schunk chigh; /* High-order bits of c */
/* Find the next non-zero chunk, setting i to its index, or break out ofloopifthereisnone.Notethatthechunkatindexuisnot necessarilynon-zero-itwasinitially,butuorthechunkatu
may have changed. */
#if OPT_CARRY
{
c = sacc->chunk[i]; if (c != 0) { goto nonzero;
}
i += 1; if (i > u) { break; /* reaching here is only possible when u == i initially, */
} /* with the last add to a chunk having changed it to 0 */
for (;;) {
c = sacc->chunk[i]; if (c != 0) { goto nonzero;
}
i += 1;
c = sacc->chunk[i]; if (c != 0) { goto nonzero;
}
i += 1;
c = sacc->chunk[i]; if (c != 0) { goto nonzero;
}
i += 1;
c = sacc->chunk[i]; if (c != 0) { goto nonzero;
}
i += 1;
}
} #else
{ do {
c = sacc->chunk[i]; if (c != 0) { goto nonzero;
}
i += 1;
} while (i <= u);
break;
} #endif
/* Propagate possible carry from this chunk to next chunk up. */
nonzero:
chigh = c >> XSUM_LOW_MANTISSA_BITS; if (chigh == 0) {
uix = i;
i += 1; continue; /* no need to change this chunk */
}
if (u == i) { if (chigh == -1) {
uix = i; break; /* don't propagate -1 into the region of all zeros above */
}
u = i + 1; /* we will change chunk[u+1], so we'll need to look at it */
}
clow = c & XSUM_LOW_MANTISSA_MASK; if (clow != 0) {
uix = i;
}
/* We now change chunk[i] and add to chunk[i+1]. Note that i+1 should be inrange(nobiggerthanXSUM_CHUNKS-1)ifsummingmemory,since thenumberofchunksisbigenoughtoholdanysum,andwedonot storeredundantchunkswithvalues0or-1abovepreviouslynon-zero chunks.Butotheraddoperationsmightcauseoverflow,inwhich caseweproduceaNaNwithall1saspayload.(Wecan'treliablyproduce
an Inf of the right sign.) */
sacc->chunk[i] = clow; if (i + 1 >= XSUM_SCHUNKS) {
xsum_small_add_inf_nan(
sacc,
((xsum_int)XSUM_EXP_MASK << XSUM_MANTISSA_BITS) | XSUM_MANTISSA_MASK);
u = i;
} else {
sacc->chunk[i + 1] +=
chigh; /* note: this could make this chunk be zero */
}
i += 1;
} while (i <= u);
if (xsum_debug) printf(" uix: %d new u: %d\n", uix, u);
/* Check again for the number being zero, since carry propagation might
have created zero from something that initially looked non-zero. */
if (uix < 0) { if (xsum_debug) printf("number is zero (2)\n");
uix = 0; goto done;
}
/* While the uppermost chunk is negative, with value -1, combine it with thechunkbelow(ifthereisone)toproducethesamenumberbutwith
one fewer non-zero chunks. */
while (sacc->chunk[uix] == -1 &&
uix > 0) { /* Left shift of a negative number is undefined according to thestandard,sodoamultiply-it'sallpresumably
constant-folded by the compiler.*/
sacc->chunk[uix - 1] +=
((xsum_schunk)-1) * (((xsum_schunk)1) << XSUM_LOW_MANTISSA_BITS);
sacc->chunk[uix] = 0;
uix -= 1;
}
/* We can now add one less than the total allowed terms before the
next carry propagate. */
/* ADD ONE NUMBER TO A SMALL ACCUMULATOR ASSUMING NO CARRY PROPAGATION REQ'D. ThisfunctionisdeclaredINLINEregardlessofthesettingofINLINE_SMALL andforgoodperformanceitmustbeinlinedbythecompiler(otherwisethe
procedure call overhead will result in substantial inefficiency). */
if (xsum_debug) {
printf(" high exp: ");
pbinary_int64(high_exp, XSUM_HIGH_EXP_BITS);
printf(" low exp: ");
pbinary_int64(low_exp, XSUM_LOW_EXP_BITS);
printf("\n");
}
/* Categorize number as normal, denormalized, or Inf/NaN according to
the value of the exponent field. */
if (exp == 0) /* zero or denormalized */
{ /* If it's a zero (positive or negative), we do nothing. */ if (mantissa == 0) { return;
} /* Denormalized mantissa has no implicit 1, but exponent is 1 not 0. */
exp = low_exp = 1;
} elseif (exp == XSUM_EXP_MASK) /* Inf or NaN */
{ /* Just update flags in accumulator structure. */
xsum_small_add_inf_nan(sacc, ivalue); return;
} else/* normalized */
{ /* OR in implicit 1 bit at top of mantissa */
mantissa |= (xsum_int)1 << XSUM_MANTISSA_BITS;
}
/* Use high part of exponent as index of chunk, and low part of exponenttogivepositionwithinchunk.Fetchthetwochunks
that will be modified. */
chunk_ptr = sacc->chunk + high_exp;
/* Separate mantissa into two parts, after shifting, and add to (or subtractfrom)thischunkandthenexthigherchunk(whichalways existssincetherearethreeextraonesatthetop).
Notethatlow_mantissawillhaveatmostXSUM_LOW_MANTISSA_BITSbits, whilehigh_mantissawillhaveatmostXSUM_MANTISSA_BITSbits,since eventhoughthehighmantissaincludestheextraimplicit1bit,itwill
also be shifted right by at least one bit. */
/* RETURN THE RESULT OF ROUNDING A SMALL ACCUMULATOR. The rounding mode istonearest,withtiestoeven.Thesmallaccumulatormaybemodified bythisoperation(bycarrypropagationbeingdone),butthevalueit
represents should not change. */
xsum_flt xsum_small_round(xsum_small_accumulator* sacc) {
xsum_int ivalue;
xsum_schunk lower; int i, j, e, more;
xsum_int intv; double fltv;
if (xsum_debug) printf("\nROUNDING SMALL ACCUMULATOR\n");
/* See if we have a NaN from one of the numbers being a NaN, in whichcasewereturntheNaNwithlargestpayload,oraninfinite result(+Inf,-Inf,oraNaNifboth+Infand-Infoccurred). NotethatwedoNOTreturnNaNifwehavebothaninfinitenumber andasumofothernumbersthatoverflowswithoppositesign,
since there is no real ambiguity regarding the sign in such a case. */
if (sacc->NaN != 0) {
COPY64(fltv, sacc->NaN); return fltv;
}
if (sacc->Inf != 0) {
COPY64(fltv, sacc->Inf); return fltv;
}
/* If none of the numbers summed were infinite or NaN, we proceed to propagatecarries,asapreliminarytofindingthemagnitudeof thesum.Thisalsoensuresthatthesignoftheresultcanbe determinedfromtheuppermostnon-zerochunk.
Wealsofindtheindex,i,ofthisuppermostnon-zerochunk,as thevaluereturnedbyxsum_carry_propagate,andsetivalueto sacc->chunk[i].Notethativaluewillnotbe0or-1,unless iis0(thelowestchunk),inwhichcaseitwillbehandledby
the code for denormalized numbers. */
i = xsum_carry_propagate(sacc);
if (xsum_debug) xsum_small_display(sacc);
ivalue = sacc->chunk[i];
/* Handle a possible denormalized number, including zero. */
if (i <= 1) { /* Check for zero value, in which case we can return immediately. */
if (ivalue == 0) { return0.0;
}
/* Check if it is actually a denormalized number. It always is if only thelowestchunkisnon-zero.Ifthehighestnon-zerochunkisthe next-to-lowest,wecheckthemagnitudeoftheabsolutevalue. Notethattherealexponentis1(not0),soweneedtoshiftright
by 1 here. */
if (i == 0) {
intv = ivalue >= 0 ? ivalue : -ivalue;
intv >>= 1; if (ivalue < 0) {
intv |= XSUM_SIGN_MASK;
} if (xsum_debug) {
printf("denormalized with i==0: intv %016llx\n", (longlong)intv);
}
COPY64(fltv, intv); return fltv;
} else { /* Note: Left shift of -ve number is undefined, so do a multiply
instead, which is probably optimized to a shift. */
intv = ivalue * ((xsum_int)1 << (XSUM_LOW_MANTISSA_BITS - 1)) +
(sacc->chunk[0] >> 1); if (intv < 0) { if (intv > -((xsum_int)1 << XSUM_MANTISSA_BITS)) {
intv = (-intv) | XSUM_SIGN_MASK; if (xsum_debug) {
printf("denormalized with i==1: intv %016llx\n", (longlong)intv);
}
COPY64(fltv, intv); return fltv;
}
} else/* non-negative */
{ if ((xsum_uint)intv < (xsum_uint)1 << XSUM_MANTISSA_BITS) { if (xsum_debug) {
printf("denormalized with i==1: intv %016llx\n", (longlong)intv);
}
COPY64(fltv, intv); return fltv;
}
} /* otherwise, it's not actually denormalized, so fall through to below */
}
}
/* Find the location of the uppermost 1 bit in the absolute value of theupperchunkbyconvertingit(asasignedinteger)toa floatingpointvalue,andlookingattheexponent.Thenset 'more'tothenumberofbitsfromthelowerchunk(andmaybethe nextlower)thatareneededtofilloutthemantissaofthe result(includingthetopimplicit1bit),plustwoextrabitsto helpdecideonrounding.Fornegativenumbers,itmayturnout laterthatweneedanotherbit,becausenegatinganegativevalue maycarryoutofthetophere,butnotcarryoutofthetoponce
more bits are shifted into the bottom later on. */
fltv = (xsum_flt)ivalue; /* finds position of topmost 1 bit of |ivalue| */
COPY64(intv, fltv);
e = (intv >> XSUM_MANTISSA_BITS) & XSUM_EXP_MASK; /* e-bias is in 0..32 */
more = 2 + XSUM_MANTISSA_BITS + XSUM_EXP_BIAS - e;
if (xsum_debug) {
printf("e: %d, more: %d, ivalue: %016llx\n", e, more,
(longlong)ivalue);
}
/* Change 'ivalue' to put in 'more' bits from lower chunks into the bottom. Alsoset'j'totheindexofthelowestchunkfromwhichthesebitscame, and'lower'totheremainingbitsofthatchunknotnowin'ivalue'. Notethat'lower'initiallyhasatleastonebitinit,whichwecan
later move into 'ivalue' if it turns out that one more bit is needed. */
ivalue *= (xsum_int)1 << more; /* multiply, since << of negative undefined */ if (xsum_debug) {
printf("after ivalue <<= more, ivalue: %016llx\n",
(longlong)ivalue);
}
j = i - 1;
lower = sacc->chunk[j]; /* must exist, since denormalized if i==0 */ if (more >= XSUM_LOW_MANTISSA_BITS) {
more -= XSUM_LOW_MANTISSA_BITS;
ivalue += lower << more; if (xsum_debug) {
printf("after ivalue += lower << more, ivalue: %016llx\n",
(longlong)ivalue);
}
j -= 1;
lower = j < 0 ? 0 : sacc->chunk[j];
}
ivalue += lower >> (XSUM_LOW_MANTISSA_BITS - more);
lower &= ((xsum_schunk)1 << (XSUM_LOW_MANTISSA_BITS - more)) - 1;
if (xsum_debug) {
printf("after final add to ivalue, ivalue: %016llx\n",
(longlong)ivalue);
printf("j: %d, e: %d, |ivalue|: %016llx, lower: %016llx (a)\n", j, e,
(longlong)(ivalue < 0 ? -ivalue : ivalue), (longlong)lower);
printf(" mask of low 55 bits: 007fffffffffffff, mask: %016llx\n",
(longlong)((xsum_schunk)1 << (XSUM_LOW_MANTISSA_BITS - more)) - 1);
}
/* Decide on rounding, with separate code for positive and negative values.
Aftersetting'ivalue'tothetentativeunsignedmantissa (shiftedleft2),and'intv'tohavethecorrectsign,this codegoestodone_roundingifitfindsthatjustdiscardinglower orderbitsiscorrect,andtoround_away_from_zeroifinsteadthe
magnitude should be increased by one in the lowest mantissa bit. */
if (ivalue >= 0) /* number is positive, lower bits are added to magnitude */
{
intv = 0; /* positive sign */
if ((ivalue & 2) == 0) /* extra bits are 0x */
{ if (xsum_debug) {
printf("+, no adjustment, since remainder adds <1/2\n");
} goto done_rounding;
}
if ((ivalue & 1) != 0) /* extra bits are 11 */
{ if (xsum_debug) {
printf("+, round away from 0, since remainder adds >1/2\n");
} goto round_away_from_zero;
}
if ((ivalue & 4) != 0) /* low bit is 1 (odd), extra bits are 10 */
{ if (xsum_debug) {
printf("+odd, round away from 0, since remainder adds >=1/2\n");
} goto round_away_from_zero;
}
if (lower == 0) /* see if any lower bits are non-zero */
{ while (j > 0) {
j -= 1; if (sacc->chunk[j] != 0) {
lower = 1; break;
}
}
}
if (lower != 0) /* low bit 0 (even), extra bits 10, non-zero lower bits */
{ if (xsum_debug) {
printf("+even, round away from 0, since remainder adds >1/2\n");
} goto round_away_from_zero;
} else/* low bit 0 (even), extra bits 10, all lower bits 0 */
{ if (xsum_debug) {
printf("+even, no adjustment, since reaminder adds exactly 1/2\n");
} goto done_rounding;
}
}
else/* number is negative, lower bits are subtracted from magnitude */
{ /* Check for a negative 'ivalue' that when negated doesn't contain a full mantissa'sworthofbits,plusonetohelprounding.Ifso,moveone morebitinto'ivalue'from'lower'(andremoveitfrom'lower'). Thishappenswhenthenegationoftheupperpartof'ivalue'hasthe
form 10000... but the negation of the full 'ivalue' is not 10000... */
if (((-ivalue) & ((xsum_int)1 << (XSUM_MANTISSA_BITS + 2))) == 0) { int pos = (xsum_schunk)1 << (XSUM_LOW_MANTISSA_BITS - 1 - more);
ivalue *= 2; /* note that left shift undefined if ivalue is negative */ if (lower & pos) {
ivalue += 1;
lower &= ~pos;
}
e -= 1; if (xsum_debug) {
printf("j: %d, e: %d, |ivalue|: %016llx, lower: %016llx (b)\n", j, e,
(longlong)(ivalue < 0 ? -ivalue : ivalue), (longlong)lower);
}
}
intv = XSUM_SIGN_MASK; /* negative sign */
ivalue = -ivalue; /* ivalue now contains the absolute value */
if ((ivalue & 3) == 3) /* extra bits are 11 */
{ if (xsum_debug) {
printf("-, round away from 0, since remainder adds >1/2\n");
} goto round_away_from_zero;
}
if ((ivalue & 3) <= 1) /* extra bits are 00 or 01 */
{ if (xsum_debug) {
printf( "-, no adjustment, since remainder adds <=1/4 or subtracts <1/4\n");
} goto done_rounding;
}
if ((ivalue & 4) == 0) /* low bit is 0 (even), extra bits are 10 */
{ if (xsum_debug) {
printf("-even, no adjustment, since remainder adds <=1/2\n");
} goto done_rounding;
}
if (lower == 0) /* see if any lower bits are non-zero */
{ while (j > 0) {
j -= 1; if (sacc->chunk[j] != 0) {
lower = 1; break;
}
}
}
if (lower != 0) /* low bit 1 (odd), extra bits 10, non-zero lower bits */
{ if (xsum_debug) {
printf("-odd, no adjustment, since remainder adds <1/2\n");
} goto done_rounding;
} else/* low bit 1 (odd), extra bits are 10, lower bits are all 0 */
{ if (xsum_debug) {
printf("-odd, round away from 0, since remainder adds exactly 1/2\n");
} goto round_away_from_zero;
}
}
round_away_from_zero:
/* Round away from zero, then check for carry having propagated out the
top, and shift if so. */
ivalue += 4; /* add 1 to low-order mantissa bit */ if (ivalue & ((xsum_int)1 << (XSUM_MANTISSA_BITS + 3))) {
ivalue >>= 1;
e += 1;
}
done_rounding:;
/* Get rid of the bottom 2 bits that were used to decide on rounding. */
ivalue >>= 2;
/* Adjust to the true exponent, accounting for where this chunk is. */
e += (i << XSUM_LOW_EXP_BITS) - XSUM_EXP_BIAS - XSUM_MANTISSA_BITS;
/* If exponent has overflowed, change to plus or minus Inf and return. */
if (e >= XSUM_EXP_MASK) {
intv |= (xsum_int)XSUM_EXP_MASK << XSUM_MANTISSA_BITS;
COPY64(fltv, intv); if (xsum_debug) {
printf("Final rounded result: %.17le (overflowed)\n ", fltv);
pbinary_double(fltv);
printf("\n");
} return fltv;
}
/* Put exponent and mantissa into intv, which already has the sign,
then copy into fltv. */
intv += (xsum_int)e << XSUM_MANTISSA_BITS;
intv += ivalue & XSUM_MANTISSA_MASK; /* mask out the implicit 1 bit */
COPY64(fltv, intv);
if (xsum_debug) {
printf("Final rounded result: %.17le\n ", fltv);
pbinary_double(fltv);
printf("\n"); if ((ivalue >> XSUM_MANTISSA_BITS) != 1) abort();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.