Changeset 543

Show
Ignore:
Timestamp:
01/15/08 03:12:46 (11 months ago)
Author:
Don Clugston
Message:

Merging improvements to Tango.Math into Phobos:
* the low level routines which operate on IEEE floating-point numbers now have versions for 64 and 80 bit reals, and for both big-endian and little-endian systems.
* internal functions isPosZero(), isNegZero() removed in favour of the more generally useful isIdentical().
* asm versions of functions which were not implemented by DMD Windows: scalb, lrint.
* added creal expi(real y) which is useful for simultaneous calculation of sin + cos.
* added comments noting the buggy/unimplemented nature of functions fma(), nan() and poly().

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • candidate/phobos/std/math.d

    r510 r543  
    6767private import std.c.math; 
    6868private import std.traits; 
     69 
     70/* Most of the functions depend on the format of the largest IEEE floating-point type. 
     71 * These code will differ depending on whether 'real' is 64, 80, or 128 bits, 
     72 * and whether it is a big-endian or little-endian architecture. 
     73 * Only three 'real' ABIs are currently supported: 
     74 * 64 bit Big-endian    (eg PowerPC) 
     75 * 64 bit Little-endian 
     76 * 80 bit Little-endian, with implied bit (eg x87, Itanium). 
     77 */ 
     78version(LittleEndian) { 
     79    static assert(real.mant_dig == 53 || real.mant_dig==64, 
     80        "Only 64-bit and 80-bit reals are supported for LittleEndian CPUs"); 
     81} else { 
     82    static assert(real.mant_dig == 53, 
     83     "Only 64-bit reals are supported for BigEndian CPUs."); 
     84} 
     85 
    6986 
    7087class NotImplemented : Error 
     
    145162unittest 
    146163{ 
    147     assert(isPosZero(abs(-0.0L))); 
     164    assert(isIdentical(abs(-0.0L), 0.0L)); 
    148165    assert(isnan(abs(real.nan))); 
    149166    assert(abs(-real.infinity) == real.infinity); 
     
    470487unittest 
    471488{ 
    472     assert(isPosZero(asinh(0.0))); 
    473     assert(isNegZero(asinh(-0.0))); 
     489    assert(isIdentical(asinh(0.0), 0.0)); 
     490    assert(isIdentical(asinh(-0.0), -0.0)); 
    474491    assert(asinh(real.infinity) == real.infinity); 
    475492    assert(asinh(-real.infinity) == -real.infinity); 
     
    502519unittest 
    503520{ 
    504     assert(isPosZero(atanh(0.0))); 
    505     assert(isNegZero(atanh(-0.0))); 
     521    assert(isIdentical(atanh(0.0), 0.0)); 
     522    assert(isIdentical(atanh(-0.0),-0.0)); 
    506523    assert(isnan(atanh(real.nan))); 
    507524    assert(isnan(atanh(-real.infinity)));  
     
    619636real expm1(real x)      { return std.c.math.expm1l(x); } 
    620637 
     638/** 
     639 * Calculate cos(y) + i sin(y). 
     640 * 
     641 * On many CPUs (such as x86), this is a very efficient operation; 
     642 * almost twice as fast as calculating sin(y) and cos(y) 
     643 * seperately, and is the preferred method when both are required. 
     644 */ 
     645creal expi(real y) 
     646{ 
     647    version(D_InlineAsm_X86) 
     648    { 
     649        asm 
     650        { 
     651            fld y; 
     652            fsincos; 
     653            fxch st(1), st(0); 
     654        } 
     655    } 
     656    else 
     657    { 
     658        return cos(y) + sin(y)*1i; 
     659    } 
     660} 
     661 
     662unittest 
     663{ 
     664    assert(expi(1.3e5L) == cos(1.3e5L) + sin(1.3e5L) * 1i); 
     665    assert(expi(0.0L) == 1L + 0.0Li); 
     666} 
    621667 
    622668/********************************************************************* 
     
    638684 */ 
    639685 
    640  
    641686real frexp(real value, out int exp) 
    642687{ 
     
    645690    uint ex; 
    646691 
    647     // If exponent is non-zero 
    648     ex = vu[4] & 0x7FFF; 
    649     if (ex) 
    650     { 
    651     if (ex == 0x7FFF) 
    652     {   // infinity or NaN 
    653         if (*vl &  0x7FFFFFFFFFFFFFFF)  // if NaN 
    654         {   *vl |= 0xC000000000000000;  // convert $(NAN)S to $(NAN)Q 
     692    static if (real.mant_dig==64) const ushort EXPMASK = 0x7FFF; 
     693                             else const ushort EXPMASK = 0x7FF0; 
     694 
     695    version(LittleEndian) { 
     696    static if (real.mant_dig==64) const int EXPONENTPOS = 4; 
     697                             else const int EXPONENTPOS = 3; 
     698    } else { // BigEndian 
     699        const int EXPONENTPOS = 0; 
     700    } 
     701 
     702    ex = vu[EXPONENTPOS] & EXPMASK; 
     703  static if (real.mant_dig == 64) { 
     704    // 80-bit reals 
     705    if (ex) { // If exponent is non-zero 
     706        if (ex == EXPMASK) {   // infinity or NaN 
     707            // 80-bit reals 
     708            if (*vl &  0x7FFFFFFFFFFFFFFF) {  // NaN 
     709                *vl |= 0xC000000000000000;  // convert $(NAN)S to $(NAN)Q 
    655710        exp = int.min; 
    656         } 
    657         else if (vu[4] & 0x8000) 
    658         {   // negative infinity 
     711            } else if (vu[EXPONENTPOS] & 0x8000) {   // negative infinity 
    659712        exp = int.min; 
    660         } 
    661         else 
    662         {   // positive infinity 
     713            } else {   // positive infinity 
    663714        exp = int.max; 
    664715        } 
    665     } 
    666     else 
    667     { 
     716        } else { 
    668717        exp = ex - 0x3FFE; 
    669         vu[4] = cast(ushort)((0x8000 & vu[4]) | 0x3FFE); 
    670     } 
    671     } 
    672     else if (!*vl) 
    673     { 
     718            vu[EXPONENTPOS] = cast(ushort)((0x8000 & vu[EXPONENTPOS]) | 0x3FFE); 
     719    } 
     720    } else if (!*vl) { 
    674721    // value is +-0.0 
    675722    exp = 0; 
    676     } 
    677     else 
    678     {   // denormal 
     723    } else { 
     724        // denormal 
    679725    int i = -0x3FFD; 
    680  
    681     do 
    682     { 
     726        do { 
     727            i--; 
     728            *vl <<= 1; 
     729        } while (*vl > 0); 
     730        exp = i; 
     731        vu[EXPONENTPOS] = cast(ushort)((0x8000 & vu[EXPONENTPOS]) | 0x3FFE); 
     732    } 
     733  } else static if(real.mant_dig==106) { 
     734    // 128-bit reals 
     735    throw new NotImplemented("frexp"); 
     736  } else { 
     737    // 64-bit reals 
     738    if (ex) { // If exponent is non-zero 
     739        if (ex == EXPMASK) {   // infinity or NaN 
     740            if (*vl==0x7FF0_0000_0000_0000) {  // positive infinity 
     741                exp = int.max; 
     742            } else if (*vl==0xFFF0_0000_0000_0000) { // negative infinity 
     743                exp = int.min; 
     744            } else { // NaN 
     745                *vl |= 0x0008_0000_0000_0000;  // convert $(NAN)S to $(NAN)Q 
     746                exp = int.min; 
     747            } 
     748        } else { 
     749            exp = (ex - 0x3FE0) >>> 4; 
     750            ve[EXPONENTPOS] = (0x8000 & ve[EXPONENTPOS]) | 0x3FE0; 
     751        } 
     752    } else if (!(*vl & 0x7FFF_FFFF_FFFF_FFFF)) { 
     753        // value is +-0.0 
     754        exp = 0; 
     755    } else { 
     756        // denormal 
     757        ushort sgn; 
     758        sgn = (0x8000 & ve[EXPONENTPOS])| 0x3FE0; 
     759        *vl &= 0x7FFF_FFFF_FFFF_FFFF; 
     760 
     761        int i = -0x3FD+11; 
     762        do { 
    683763        i--; 
    684764        *vl <<= 1; 
    685765    } while (*vl > 0); 
    686766    exp = i; 
    687         vu[4] = cast(ushort)((0x8000 & vu[4]) | 0x3FFE); 
     767        ve[EXPONENTPOS] = sgn; 
     768    } 
    688769    } 
    689770    return value; 
     
    700781    [-1.0,  -.5,    1], 
    701782    [2.0,   .5, 2], 
    702     [155.67e20, 0x1.A5F1C2EB3FE4Fp-1,   74],    // normal 
    703     [1.0e-320,  0.98829225,     -1063], 
    704     [real.min,  .5,     -16381], 
    705     [real.min/2.0L, .5,     -16382],    // denormal 
    706  
     783        [double.min/2.0, .5, -1022], 
    707784    [real.infinity,real.infinity,int.max], 
    708785    [-real.infinity,-real.infinity,int.min], 
    709786    [real.nan,real.nan,int.min], 
    710787    [-real.nan,-real.nan,int.min], 
    711  
    712     // Don't really support signalling nan's in D 
    713     //[real.nans,real.nan,int.min], 
    714     //[-real.nans,-real.nan,int.min], 
    715788    ]; 
     789 
    716790    int i; 
    717791 
    718     for (i = 0; i < vals.length; i++) 
    719     { 
     792    for (i = 0; i < vals.length; i++) { 
    720793    real x = vals[i][0]; 
    721794    real e = vals[i][1]; 
     
    723796    int eptr; 
    724797    real v = frexp(x, eptr); 
    725  
    726     //printf("frexp(%Lg) = %.8Lg, should be %.8Lg, eptr = %d, should be %d\n", x, v, e, eptr, exp); 
    727     assert(mfeq(e, v, .0000001)); 
     798//        printf("frexp(%La) = %La, should be %La, eptr = %d, should be %d\n", x, v, e, eptr, exp); 
     799        assert(isIdentical(e, v)); 
     800        assert(exp == eptr); 
     801 
     802    } 
     803   static if (real.mant_dig == 64) { 
     804     static real extendedvals[][3] = [ // x,frexp,exp 
     805        [0x1.a5f1c2eb3fe4efp+73, 0x1.A5F1C2EB3FE4EFp-1,   74],    // normal 
     806        [0x1.fa01712e8f0471ap-1064,  0x1.fa01712e8f0471ap-1,     -1063], 
     807        [real.min,  .5,     -16381], 
     808        [real.min/2.0L, .5,     -16382]    // denormal 
     809     ]; 
     810 
     811    for (i = 0; i < extendedvals.length; i++) { 
     812        real x = extendedvals[i][0]; 
     813        real e = extendedvals[i][1]; 
     814        int exp = cast(int)extendedvals[i][2]; 
     815        int eptr; 
     816        real v = frexp(x, eptr); 
     817        assert(isIdentical(e, v)); 
    728818    assert(exp == eptr); 
    729     } 
    730 
    731  
     819 
     820    } 
     821    } 
     822
    732823 
    733824/****************************************** 
     
    861952    version (linux) 
    862953    return std.c.math.scalbnl(x, n); 
    863     else 
     954    else version(D_InlineAsm_X86) { 
     955        asm { 
     956            fild n; 
     957            fld x; 
     958            fscale; 
     959            fstp st(1), st; 
     960        } 
     961    } else 
    864962    throw new NotImplemented("scalbn"); 
     963} 
     964 
     965unittest { 
     966    assert(scalbn(-real.infinity, 5) == -real.infinity); 
    865967} 
    866968 
     
    11131215 * Rounds x to the nearest integer value, using the current rounding 
    11141216 * mode. 
     1217 * 
     1218 * This is generally the fastest method to convert a floating-point number 
     1219 * to an integer. Note that the results from this function 
     1220 * depend on the rounding mode, if the fractional part of x is exactly 0.5. 
     1221 * If using the default rounding mode (ties round to even integers) 
     1222 * lrint(4.5) == 4, lrint(5.5)==6. 
    11151223 */ 
    11161224long lrint(real x) 
     
    11181226    version (linux) 
    11191227    return std.c.math.llrintl(x); 
     1228    else version(D_InlineAsm_X86) 
     1229    { 
     1230        long n; 
     1231        asm 
     1232        { 
     1233            fld x; 
     1234            fistp n; 
     1235        } 
     1236        return n; 
     1237    } 
    11201238    else 
    11211239    throw new NotImplemented("lrint"); 
     
    11841302 */ 
    11851303 
    1186 int isnan(real e) 
    1187 
    1188     ushort* pe = cast(ushort *)&e; 
    1189     ulong*  ps = cast(ulong *)&e; 
     1304int isnan(real x) 
     1305
     1306  static if (real.mant_dig==double.mant_dig) { 
     1307        // 64-bit real 
     1308        ulong*  p = cast(ulong *)&x; 
     1309        return (*p & 0x7FF0_0000 == 0x7FF0_0000) && *p & 0x000F_FFFF; 
     1310  } else { 
     1311        // 80-bit real 
     1312        ushort* pe = cast(ushort *)&x; 
     1313        ulong*  ps = cast(ulong *)&x; 
    11901314 
    11911315    return (pe[4] & 0x7FFF) == 0x7FFF && 
    11921316        *ps & 0x7FFFFFFFFFFFFFFF; 
     1317} 
    11931318} 
    11941319 
     
    12091334int isfinite(real e) 
    12101335{ 
     1336    static if (real.mant_dig == double.mant_dig) { 
     1337        return isfinite(cast(double)e); 
     1338    } else { 
    12111339    ushort* pe = cast(ushort *)&e; 
    12121340 
    12131341    return (pe[4] & 0x7FFF) != 0x7FFF; 
     1342} 
    12141343} 
    12151344 
     
    12531382/// ditto 
    12541383 
    1255 int isnormal(real e) 
    1256 
    1257     ushort* pe = cast(ushort *)&e; 
    1258     long*   ps = cast(long *)&e; 
     1384int isnormal(real x) 
     1385
     1386    static if (real.mant_dig == double.mant_dig) { 
     1387        return isNormal(cast(double)x); 
     1388    } else { 
     1389        ushort* pe = cast(ushort *)&x; 
     1390        long*   ps = cast(long *)&x; 
    12591391 
    12601392    return (pe[4] & 0x7FFF) != 0x7FFF && *ps < 0; 
     1393} 
    12611394} 
    12621395 
     
    13181451int issubnormal(real e) 
    13191452{ 
     1453    static if (real.mant_dig == double.mant_dig) { 
     1454        return isSubnormal(cast(double)e); 
     1455    } else { 
    13201456    ushort* pe = cast(ushort *)&e; 
    13211457    long*   ps = cast(long *)&e; 
     
    13231459    return (pe[4] & 0x7FFF) == 0 && *ps > 0; 
    13241460} 
     1461} 
    13251462 
    13261463unittest 
     
    13381475int isinf(real e) 
    13391476{ 
     1477    static if (real.mant_dig == double.mant_dig) { 
     1478        return ((*cast(ulong *)&x)&0x7FFF_FFFF_FFFF_FFFF) == 0x7FF8_0000_0000_0000; 
     1479    } else { 
    13401480    ushort* pe = cast(ushort *)&e; 
    13411481    ulong*  ps = cast(ulong *)&e; 
    13421482 
    13431483    return (pe[4] & 0x7FFF) == 0x7FFF && 
    1344         *ps == 0x8000000000000000; 
     1484            *ps == 0x8000_0000_0000_0000; 
     1485   } 
    13451486} 
    13461487 
     
    13561497 
    13571498/********************************* 
     1499 * Is the binary representation of x identical to y? 
     1500 * 
     1501 * Same as ==, except that positive and negative zero are not identical, 
     1502 * and two $(NAN)s are identical if they have the same 'payload'. 
     1503 */ 
     1504 
     1505bool isIdentical(real x, real y) 
     1506{ 
     1507    long*   pxs = cast(long *)&x; 
     1508    long*   pys = cast(long *)&y; 
     1509  static if (real.mant_dig == double.mant_dig){ 
     1510    return pxs[0] == pys[0]; 
     1511  } else { 
     1512    ushort* pxe = cast(ushort *)&x; 
     1513    ushort* pye = cast(ushort *)&y; 
     1514    return pxe[4] == pye[4] && pxs[0] == pys[0]; 
     1515  } 
     1516} 
     1517 
     1518/********************************* 
    13581519 * Return 1 if sign bit of e is set, 0 if not. 
    13591520 */ 
    13601521 
    1361 int signbit(real e) 
    1362 
    1363     ubyte* pe = cast(ubyte *)&e; 
    1364  
    1365 //printf("e = %Lg\n", e); 
     1522int signbit(real x) 
     1523
     1524    static if (real.mant_dig == double.mant_dig) { 
     1525        return ((*cast(ulong *)&x) & 0x8000_0000_0000_0000) != 0; 
     1526    } else { 
     1527        ubyte* pe = cast(ubyte *)&x; 
    13661528    return (pe[9] & 0x80) != 0; 
     1529} 
    13671530} 
    13681531 
     
    14151578/****************************************** 
    14161579 * Creates a quiet NAN with the information from tagp[] embedded in it. 
     1580 * 
     1581 * BUGS: DMD always returns real.nan, ignoring the payload. 
    14171582 */ 
    14181583real nan(char[] tagp) { return std.c.math.nanl(toStringz(tagp)); } 
     
    14851650 * Returns (x * y) + z, rounding only once according to the 
    14861651 * current rounding mode. 
     1652 * 
     1653 * BUGS: Not currently implemented - rounds twice. 
    14871654 */ 
    14881655real fma(real x, real y, real z) { return (x * y) + z; } 
     
    17041871} 
    17051872 
    1706 // Returns true if x is +0.0 (This function is used in unit tests) 
    1707 bool isPosZero(real x) 
    1708 { 
    1709     return (x == 0) && (signbit(x) == 0); 
    1710 } 
    1711  
    1712 // Returns true if x is -0.0 (This function is used in unit tests) 
    1713 bool isNegZero(real x) 
    1714 { 
    1715     return (x == 0) && signbit(x); 
    1716 } 
    1717  
    17181873/************************************** 
    17191874 * To what precision is x equal to y? 
     
    17361891    /* Public Domain. Author: Don Clugston, 18 Aug 2005. 
    17371892     */ 
    1738  
    17391893    if (x == y) 
    17401894    return real.mant_dig; // ensure diff!=0, cope with INF. 
     
    17551909    // always 1 lower than we want, except that if bitsdiff==0, 
    17561910    // they could have 0 or 1 bits in common. 
    1757     int bitsdiff = ( ((pa[4]&0x7FFF) + (pb[4]&0x7FFF)-1)>>1) - pd[4]; 
    1758  
    1759     if (pd[4] == 0) 
     1911 static if (real.mant_dig==64) { // 80-bit reals 
     1912    const real POW2MANTDIG = 0x1p+63; // pow(2, real.mant_dig) 
     1913    const int EXPONENTPOS = 4; 
     1914    int bitsdiff = ( ((pa[EXPONENTPOS]&0x7FFF) + (pb[EXPONENTPOS]&0x7FFF)-1)>>1)  
     1915                 - pd[EXPONENTPOS]; 
     1916 } else {  // 64-bit reals 
     1917    const real POW2MANTDIG = 0x1p+53; 
     1918      version(LittleEndian) 
     1919        const int EXPONENTPOS = 3; 
     1920    else const int EXPONENTPOS = 0; 
     1921    int bitsdiff = ( ((pa[EXPONENTPOS]&0x7FF0) + (pb[EXPONENTPOS]&0x7FF0)-0x10)>>5)  
     1922                 - (pd[EXPONENTPOS]&0x7FF0>>4); 
     1923 } 
     1924    if (pd[EXPONENTPOS] == 0) 
    17601925    {   // Difference is denormal 
    17611926    // For denormals, we need to add the number of zeros that 
    1762    // lie at the start of diff's mantissa
     1927        // lie at the start of diff's significand
    17631928    // We do this by multiplying by 2^real.mant_dig 
    1764    diff *= 0x1p+63
    1765    return bitsdiff + real.mant_dig - pd[4]; 
     1929        diff *= POW2MANTDIG
     1930        return bitsdiff + real.mant_dig - pd[EXPONENTPOS]; 
    17661931    } 
    17671932 
     
    17701935 
    17711936    // Avoid out-by-1 errors when factor is almost 2. 
    1772     return (bitsdiff == 0) ? (pa[4] == pb[4]) : 0; 
     1937 static if (real.mant_dig==64) 
     1938 { 
     1939    return (bitsdiff == 0) ? (pa[EXPONENTPOS] == pb[EXPONENTPOS]) : 0; 
     1940 } else { 
     1941    if (bitsdiff == 0 && (pa[EXPONENTPOS] ^ pb[EXPONENTPOS])&0x7FF0) return 1; 
     1942    else return 0; 
     1943 } 
    17731944} 
    17741945 
     
    18342005    version (Windows) 
    18352006    { 
     2007        // BUG: This code assumes a frame pointer in EBP. 
    18362008        asm // assembler by W. Bright 
    18372009        {