QDouble.st
author Claus Gittinger <cg@exept.de>
Wed, 14 Jun 2017 11:46:10 +0200
changeset 4387 879309cae427
parent 4386 0a320155d78a
child 4388 742f099741bf
permissions -rw-r--r--
#REFACTORING by cg class: QDouble class definition comment/format in: #productFromFloat: #quotientFromQDouble: changed: #productFromQDouble: #sumFromFloat: #sumFromQDouble:

"
 COPYRIGHT (c) 2017 by eXept Software AG
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
"{ Package: 'stx:libbasic2' }"

"{ NameSpace: Smalltalk }"

LimitedPrecisionReal variableByteSubclass:#QDouble
	instanceVariableNames:''
	classVariableNames:'DefaultPrintFormat QDoubleZero QDoubleOne Pi E Ln2 Ln10 Epsilon'
	poolDictionaries:''
	category:'Magnitude-Numbers'
!

!QDouble primitiveDefinitions!

%{
#include <stdio.h>
#include <errno.h>

#define __USE_ISOC9X 1
#define __USE_ISOC99 1
#include <math.h>

/*
 * on some systems, errno is a macro ... check for it here
 */
#ifndef errno
 extern errno;
#endif

#if !defined (__win32__)
# include <locale.h>
#endif

#if defined (__aix__)
# include <float.h>
#endif

#if defined(__irix__) || defined(__solaris__) || defined(__sunos__)
# include <nan.h>
#endif

#if defined(__linux__)
# ifndef NAN
#  include <bits/nan.h>
# endif
#endif

#if defined(__x86__) || defined(__x86_64__)

# ifndef _FPU_EXTENDED
#  define _FPU_EXTENDED 0x0300
# endif

# ifndef _FPU_DOUBLE
#  define _FPU_DOUBLE 0x0200
# endif

# if defined( __win32__ ) && (defined( __BORLANDC__ ) || defined( __VISUALC__ )) 

#  define fpu_fix_start(old_cw_ptr)\
    {\
        *old_cw_ptr = _control87(0, 0); \        
        _control87(_FPU_DOUBLE, _FPU_EXTENDED);\
    }

#  define fpu_fix_end(old_cw_ptr)\
    {\
        _control87(*old_cw_ptr, _FPU_EXTENDED);\
    }

# else // assume MINGW, GCC or CLANG

#  ifndef _FPU_GETCW
#   define _FPU_GETCW(x) asm volatile ("fnstcw %0":"=m" (x));
#  endif
#  ifndef _FPU_SETCW
#   define _FPU_SETCW(x) asm volatile ("fldcw %0": :"m" (x));
#  endif

#  define fpu_fix_start(old_cw_ptr)\
    {\
        volatile unsigned short cw, new_cw;\
        _FPU_GETCW(cw);\
        new_cw = (cw & ~_FPU_EXTENDED) | _FPU_DOUBLE;\
        _FPU_SETCW(new_cw);\
        *old_cw_ptr = cw;\
    }

#  define fpu_fix_end(old_cw_ptr)\
    {\
        volatile unsigned short cw = *old_cw_ptr;\
        _FPU_SETCW(cw);\
    }

# endif

#endif


struct qd_real {
    double x[4];    /* The Components. */
};

struct __quadDoubleStruct {
        STX_OBJ_HEADER
#ifdef __NEED_DOUBLE_ALIGN
        __FILLTYPE_DOUBLE f_filler;
#endif
        double d_quadDoubleValue[4];
};

#define __QuadDoubleInstPtr(obj)      ((struct __quadDoubleStruct *)(__objPtr(obj)))

#ifndef __isQuadDouble
# define __isQuadDouble(o) \
        (__Class(o) == @global(QuadDouble))
#endif

#ifndef __qIsQuadDouble
# define __qIsQuadDouble(o) \
        (__qClass(o) == @global(QuadDouble))
#endif

#define __qNew_qdReal(newQD, d0,d1,d2,d3) { \
    __qNew(newQD, sizeof(struct __quadDoubleStruct)); \
    __stx_setClass(newQD, QDouble);                \
    __QuadDoubleInstPtr(newQD)->d_quadDoubleValue[0] = d0;   \
    __QuadDoubleInstPtr(newQD)->d_quadDoubleValue[1] = d1;   \
    __QuadDoubleInstPtr(newQD)->d_quadDoubleValue[2] = d2;   \
    __QuadDoubleInstPtr(newQD)->d_quadDoubleValue[3] = d3;   \
}

// qd_real(c0, c1, c2, c3);

#define _QD_SPLITTER 134217729.0               // = 2^27 + 1
#define _QD_SPLIT_THRESH 6.69692879491417e+299 // = 2^996

#define m_quick_two_sum(rslt, a, b, err) {\
  double s = a + b;\
  err = b - (s - a);\
  rslt = s; \
}

#define m_quick_two_diff(rslt, a, b, err) {\
  double s = a - b;\
  err = (a - s) - b;\
  rslt = s;\
}

#define m_two_sum(rslt, a, b, err) {\
  double s = a + b;\
  double bb = s - a;\
  err = (a - (s - bb)) + (b - bb);\
  rslt = s;\
}

/* Computes fl(a-b) and err(a-b).  */
#define m_two_diff(rslt, a, b, err) {\
  double s = a - b;\
  double bb = s - a;\
  err = (a - (s - bb)) - (b + bb);\
  rslt = s;\
}

#define m_three_sum(a, b, c) { \
  double t1, t2, t3; \
  m_two_sum(t1, a, b, t2); \
  m_two_sum(a, c, t1, t3); \
  m_two_sum(b, t2, t3, c); \
}

#define m_three_sum2(a, b, c) {\
  double t1, t2, t3;\
  m_two_sum(t1, a, b, t2);\
  m_two_sum(a, c, t1, t3);\
  b = t2 + t3;\
}

#ifndef QD_FMS

/* Computes high word and lo word of a */
#define m_split(a, hi, lo) {\
  double temp;\
  if (a > _QD_SPLIT_THRESH || a < -_QD_SPLIT_THRESH) {\
    a *= 3.7252902984619140625e-09;\
    temp = _QD_SPLITTER * a;\
    hi = temp - (temp - a);\
    lo = a - hi;\
    hi *= 268435456.0;\
    lo *= 268435456.0;\
  } else {\
    temp = _QD_SPLITTER * a;\
    hi = temp - (temp - a);\
    lo = a - hi;\
  }\
}

#endif


#ifdef QD_FMS

/* Computes fl(a*b) and err(a*b). */

#define m_two_prod(rslt, a, b, err) {\
  double p = a * b;\
  err = QD_FMS(a, b, p);\
  rslt = p; \
}

#else

#define m_two_prod(rslt, a, b, err) {\
  double a_hi, a_lo, b_hi, b_lo;\
  double p = a * b;\
  m_split(a, a_hi, a_lo);\
  m_split(b, b_hi, b_lo);\
  err = ((a_hi * b_hi - p) + a_hi * b_lo + a_lo * b_hi) + a_lo * b_lo;\
  rslt = p; \
}

#endif

#ifdef QD_FMS

#define m_two_sqr(rslt, a, err) {\
  double p = a * a;\
  err = QD_FMS(a, a, p);\
  rslt = p;\
}

#else

#define m_two_sqr(rslt, a, err) {\
  double hi, lo;\
  double q = a * a;\
  m_split(a, hi, lo);\
  err = ((hi * hi - q) + 2.0 * hi * lo) + lo * lo;\
  rslt = q;\
}

#endif

#define m_renorm4(c0, c1, c2, c3) {\
  double s0, s1, s2 = 0.0, s3 = 0.0;\
\
    if (! isinf(c0)) { \
\
        m_quick_two_sum(s0, c2, c3, c3);\
        m_quick_two_sum(s0, c1, s0, c2);\
        m_quick_two_sum(c0, c0, s0, c1);\
\
        s0 = c0;\
        s1 = c1;\
        if (s1 != 0.0) {\
             m_quick_two_sum(s1, s1, c2, s2);\
            if (s2 != 0.0) {\
                 m_quick_two_sum(s2, s2, c3, s3);\
            } else {\
                 m_quick_two_sum(s1, s1, c3, s2);\
            }\
        } else {\
            m_quick_two_sum(s0, s0, c2, s1);\
            if (s1 != 0.0) {\
                 m_quick_two_sum(s1, s1, c3, s2);\
            } else {\
                 m_quick_two_sum(s0, s0, c3, s1);\
            }\
        }\
\
        c0 = s0;\
        c1 = s1;\
        c2 = s2;\
        c3 = s3;\
    }\
}

#define m_renorm5(c0, c1, c2, c3, c4) { \
    double s0, s1, s2 = 0.0, s3 = 0.0; \
\
    if (! isinf(c0)) { \
        m_two_sum(s0, c3, c4, c4); \
        m_quick_two_sum(s0, c2, s0, c3); \
        m_quick_two_sum(s0, c1, s0, c2); \
        m_quick_two_sum(c0, c0, s0, c1); \
\
        s0 = c0; \
        s1 = c1; \
\
        m_two_sum(s0, c0, c1, s1); \
        if (s1 != 0.0) { \
            m_quick_two_sum(s1, s1, c2, s2); \
            if (s2 != 0.0) { \
                m_quick_two_sum(s2 ,s2, c3, s3); \
                if (s3 != 0.0) {\
                    s3 += c4; \
                } else {\
                    s2 += c4;\
                }\
            } else { \
                m_quick_two_sum(s1, s1, c3, s2); \
                if (s2 != 0.0) {\
                    m_quick_two_sum(s2, s2, c4, s3); \
                } else { \
                    m_quick_two_sum(s1, s1, c4, s2); \
                } \
            } \
        } else { \
            m_quick_two_sum(s0,s0, c2, s1); \
            if (s1 != 0.0) { \
                m_quick_two_sum(s1,s1, c3, s2); \
                if (s2 != 0.0) {\
                    m_quick_two_sum(s2,s2, c4, s3); \
                } else { \
                    m_quick_two_sum(s1 ,s1, c4, s2); \
                } \
            } else { \
                m_quick_two_sum(s0,s0, c3, s1); \
                if (s1 != 0.0) { \
                    m_quick_two_sum(s1,s1, c4, s2); \
                } else { \
                    m_quick_two_sum(s0,s0, c4, s1); \
                } \
            } \
        } \
 \
        c0 = s0; \
        c1 = s1; \
        c2 = s2; \
        c3 = s3; \
    } \
}

%}
! !

!QDouble primitiveFunctions!
%{

/*********** Basic Functions ************/
/* Computes fl(a+b) and err(a+b).  Assumes |a| >= |b|. */
inline double 
quick_two_sum(double a, double b, double *errPtr) {
  double s = a + b;
  *errPtr = b - (s - a);
  return s;
}

/* Computes fl(a-b) and err(a-b).  Assumes |a| >= |b| */
inline double 
quick_two_diff(double a, double b, double *errPtr) {
  double s = a - b;
  *errPtr = (a - s) - b;
  return s;
}

/* Computes fl(a+b) and err(a+b).  */
inline double 
two_sum(double a, double b, double *errPtr) {
  double s = a + b;
  double bb = s - a;
  *errPtr = (a - (s - bb)) + (b - bb);
  return s;
}

/* Computes fl(a-b) and err(a-b).  */
inline double 
two_diff(double a, double b, double *errPtr) {
  double s = a - b;
  double bb = s - a;
  *errPtr = (a - (s - bb)) - (b + bb);
  return s;
}

#ifndef QD_FMS
/* Computes high word and lo word of a */
inline void 
split(double a, double *hiPtr, double *loPtr) {
  double temp;
  if (a > _QD_SPLIT_THRESH || a < -_QD_SPLIT_THRESH) {
    a *= 3.7252902984619140625e-09;  // 2^-28
    temp = _QD_SPLITTER * a;
    *hiPtr = temp - (temp - a);
    *loPtr = a - *hiPtr;
    *hiPtr *= 268435456.0;          // 2^28
    *loPtr *= 268435456.0;          // 2^28
  } else {
    temp = _QD_SPLITTER * a;
    *hiPtr = temp - (temp - a);
    *loPtr = a - *hiPtr;
  }
}
#endif

/* Computes fl(a*b) and err(a*b). */
inline double 
two_prod(double a, double b, double *errPtr) {
#ifdef QD_FMS
  double p = a * b;
  *errPtr = QD_FMS(a, b, p);
  return p;
#else
  double a_hi, a_lo, b_hi, b_lo;
  double p = a * b;
  split(a, &a_hi, &a_lo);
  split(b, &b_hi, &b_lo);
  *errPtr = ((a_hi * b_hi - p) + a_hi * b_lo + a_lo * b_hi) + a_lo * b_lo;
  return p;
#endif
}

/* Computes fl(a*a) and err(a*a).  Faster than the above method. */
inline double 
two_sqr(double a, double *errPtr) {
#ifdef QD_FMS
  double p = a * a;
  *errPtr = QD_FMS(a, a, p);
  return p;
#else
  double hi, lo;
  double q = a * a;
  split(a, &hi, &lo);
  *errPtr = ((hi * hi - q) + 2.0 * hi * lo) + lo * lo;
  return q;
#endif
}

/* Computes the nearest integer to d. */
inline double 
nint(double d) {
  if (d == floor(d))
    return d;
  return floor(d + 0.5);
}

/* Computes the truncated integer. */
inline double 
aint(double d) {
  return (d >= 0.0) ? floor(d) : ceil(d);
}

inline void 
renorm4(double *c0Ptr, double *c1Ptr, double *c2Ptr, double *c3Ptr) {
  double s0, s1, s2 = 0.0, s3 = 0.0;
  double c0 = *c0Ptr;
  
  if (isinf(c0)) return;

  s0 = quick_two_sum(*c2Ptr, *c3Ptr, c3Ptr);
  s0 = quick_two_sum(*c1Ptr, s0, c2Ptr);
  c0 = quick_two_sum(c0, s0, c1Ptr);

  s0 = c0;
  s1 = *c1Ptr;
  if (s1 != 0.0) {
    s1 = quick_two_sum(s1, *c2Ptr, &s2);
    if (s2 != 0.0)
      s2 = quick_two_sum(s2, *c3Ptr, &s3);
    else
      s1 = quick_two_sum(s1, *c3Ptr, &s2);
  } else {
    s0 = quick_two_sum(s0, *c2Ptr, &s1);
    if (s1 != 0.0)
      s1 = quick_two_sum(s1, *c3Ptr, &s2);
    else
      s0 = quick_two_sum(s0, *c3Ptr, &s1);
  }

  *c0Ptr = s0;
  *c1Ptr = s1;
  *c2Ptr = s2;
  *c3Ptr = s3;
}

inline void 
renorm5(double *c0Ptr, double *c1Ptr, double *c2Ptr, double *c3Ptr, double *c4Ptr) {
  double s0, s1, s2 = 0.0, s3 = 0.0;
  
  if (isinf(*c0Ptr)) return;

  s0 = quick_two_sum(*c3Ptr, *c4Ptr, c4Ptr);
  s0 = quick_two_sum(*c2Ptr, s0, c3Ptr);
  s0 = quick_two_sum(*c1Ptr, s0, c2Ptr);
  *c0Ptr = quick_two_sum(*c0Ptr, s0, c1Ptr);

  s0 = *c0Ptr;
  s1 = *c1Ptr;

  s0 = quick_two_sum(*c0Ptr, *c1Ptr, &s1);
  if (s1 != 0.0) {
    s1 = quick_two_sum(s1, *c2Ptr, &s2);
    if (s2 != 0.0) {
      s2 =quick_two_sum(s2, *c3Ptr, &s3);
      if (s3 != 0.0)
        s3 += *c4Ptr;
      else
        s2 += *c4Ptr;
    } else {
      s1 = quick_two_sum(s1, *c3Ptr, &s2);
      if (s2 != 0.0)
        s2 = quick_two_sum(s2, *c4Ptr, &s3);
      else
        s1 = quick_two_sum(s1, *c4Ptr, &s2);
    }
  } else {
    s0 = quick_two_sum(s0, *c2Ptr, &s1);
    if (s1 != 0.0) {
      s1 = quick_two_sum(s1, *c3Ptr, &s2);
      if (s2 != 0.0)
        s2 = quick_two_sum(s2, *c4Ptr, &s3);
      else
        s1 = quick_two_sum(s1, *c4Ptr, &s2);
    } else {
      s0 = quick_two_sum(s0, *c3Ptr, &s1);
      if (s1 != 0.0)
        s1 = quick_two_sum(s1, *c4Ptr, &s2);
      else
        s0 = quick_two_sum(s0, *c4Ptr, &s1);
    }
  }

  *c0Ptr = s0;
  *c1Ptr = s1;
  *c2Ptr = s2;
  *c3Ptr = s3;
}

inline void 
three_sum(double *aPtr, double *bPtr, double *cPtr) {
  double t1, t2, t3;
  t1 = two_sum(*aPtr, *bPtr, &t2);
  *aPtr  = two_sum(*cPtr, t1, &t3);
  *bPtr  = two_sum(t2, t3, cPtr);
}

inline void three_sum2(double *aPtr, double *bPtr, double *cPtr) {
  double t1, t2, t3;
  t1 = two_sum(*aPtr, *bPtr, &t2);
  *aPtr  = two_sum(*cPtr, t1, &t3);
  *bPtr = t2 + t3;
}


#if 0
/* These are provided to give consistent 
   interface for double with double-double and quad-double. */
inline void 
sincosh(double t, double &sinh_t, double &cosh_t) {
  sinh_t = sinh(t);
  cosh_t = cosh(t);
}

inline double 
sqr(double t) {
  return t * t;
}

inline double 
to_double(double a) { 
    return a; 
}

inline int    
to_int(double a)    { 
    return static_cast<int>(a); 
}
#endif

%}
! !

!QDouble class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2017 by eXept Software AG
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    QDoubles represent rational numbers with extended, but still limited precision.

    In contrast to Floats (which use the C-compilers native 64bit 'double' format),
    QDoubles give you 212 bit or approx. 64 decimal digits of precision.

    Representation:
        QDoubles use 4 IEEE doubles, each keeping 53 bits of precision.

    Range and Precision of Storage Formats: see LimitedPrecisionReal >> documentation

    [author:]
        Claus Gittinger

    [see also:]
        Number
        Float ShortFloat Fraction FixedPoint Integer Complex
        FloatArray DoubleArray
"
! !

!QDouble class methodsFor:'instance creation'!

basicNew
    "return a new quad-precision double - here we return 0.0
     Notice that numbers are usually NOT created this way ...
     It's implemented here to allow things like binary store & load
     of floats. (but even this support will go away eventually, it's not
     a good idea to store the bits of a float - the reader might have a
     totally different representation - so floats should be
     binary stored in a device independent format."

%{  /* NOCONTEXT */
#ifdef __SCHTEAM__
    ERROR("trying to instantiate a quad double");
#else
    OBJ newFloat;
printf("xxx\n");
    __qNew(newFloat, sizeof(struct __quadDoubleStruct));              
    __stx_setClass(newFloat, QDouble);                         
    __QuadDoubleInstPtr(newFloat)->d_quadDoubleValue[0] = 0.0;        
    __QuadDoubleInstPtr(newFloat)->d_quadDoubleValue[1] = 0.0;        
    __QuadDoubleInstPtr(newFloat)->d_quadDoubleValue[2] = 0.0;        
    __QuadDoubleInstPtr(newFloat)->d_quadDoubleValue[3] = 0.0;        
    RETURN (newFloat);
#endif /* not SCHTEAM */
%}

    "
     self basicNew
    "

    "Created: / 12-06-2017 / 16:00:38 / cg"
!

d0:d0 d1:d1 d2:d2 d3:d3
    "return a new quad-precision double from individual double components"

%{  /* NOCONTEXT */
#ifdef __SCHTEAM__
    ERROR("trying to instantiate a quad double");
#else
    OBJ newQD;

    if (__isFloatLike(d0) 
     && __isFloatLike(d1)
     && __isFloatLike(d2)
     && __isFloatLike(d3)) {    
        __qNew_qdReal(newQD, __floatVal(d0), __floatVal(d1),        
                             __floatVal(d2), __floatVal(d3));        
        RETURN (newQD);
    }
#endif
%}.
    self error:'invalid argument'

    "
     self d0: 3.141592653589793116e+00
          d1: 1.224646799147353207e-16
          d2: -2.994769809718339666e-33
          d3: 1.112454220863365282e-49
    "

    "Created: / 12-06-2017 / 20:17:14 / cg"
!

fromDoubleArray:aDoubleArray
    "return a new quad-precision double from coercing a double array"

%{  /* NOCONTEXT */
#ifdef __SCHTEAM__
    ERROR("trying to instantiate a quad double");
#else
    OBJ newQD;

    if (__isDoubleArray(aDoubleArray)) {    
        __qNew_qdReal(newQD, 
                    __DoubleArrayInstPtr(aDoubleArray)->d_element[0],        
                    __DoubleArrayInstPtr(aDoubleArray)->d_element[1],        
                    __DoubleArrayInstPtr(aDoubleArray)->d_element[2],        
                    __DoubleArrayInstPtr(aDoubleArray)->d_element[3]);        
        RETURN (newQD);
    }
#endif
%}.
    self error:'invalid argument'

    "
     self fromDoubleArray(DoubleArray 
                                with: 3.141592653589793116e+00
                                with: 1.224646799147353207e-16
                                with: -2.994769809718339666e-33
                                with: 1.112454220863365282e-49)
    "

    "Created: / 12-06-2017 / 18:25:32 / cg"
!

fromFloat:aFloat
    "return a new quad-precision double from coercing aFloat"

%{  /* NOCONTEXT */
#ifdef __SCHTEAM__
    ERROR("trying to instantiate a quad double");
#else
    double dVal;
    OBJ newFloat;

    if (__isFloatLike(aFloat)) {
        dVal = __floatVal(aFloat);
    } else if (__isShortFloat(aFloat)) {       
        dVal = __shortFloatVal(aFloat);
    } else {
        goto badArg;
    }    
        
    __qNew(newFloat, sizeof(struct __quadDoubleStruct));              
    __stx_setClass(newFloat, QDouble);                         
    __QuadDoubleInstPtr(newFloat)->d_quadDoubleValue[0] = dVal;        
    __QuadDoubleInstPtr(newFloat)->d_quadDoubleValue[1] = 0.0;        
    __QuadDoubleInstPtr(newFloat)->d_quadDoubleValue[2] = 0.0;        
    __QuadDoubleInstPtr(newFloat)->d_quadDoubleValue[3] = 0.0;        
    RETURN (newFloat);

badArg: ;

#endif
%}.
    self error:'invalid argument'

    "
     self fromFloat:1.0
    "

    "Created: / 12-06-2017 / 16:06:54 / cg"
!

fromInteger:anInteger
    "return a new quad-precision double from coercing anInteger"

%{  /* NOCONTEXT */
#ifndef __SCHTEAM__
    OBJ newFloat;

    if (__isSmallInteger(anInteger)) {
        __qNew(newFloat, sizeof(struct __quadDoubleStruct));              
        __stx_setClass(newFloat, QDouble);                         
        __QuadDoubleInstPtr(newFloat)->d_quadDoubleValue[0] = (double)(__smallIntegerVal(anInteger));        
        __QuadDoubleInstPtr(newFloat)->d_quadDoubleValue[1] = 0.0;        
        __QuadDoubleInstPtr(newFloat)->d_quadDoubleValue[2] = 0.0;        
        __QuadDoubleInstPtr(newFloat)->d_quadDoubleValue[3] = 0.0;        
        RETURN (newFloat);
    }
#endif
%}.
    ^ super fromInteger:anInteger

    "
     self fromInteger:2
    "

    "Created: / 12-06-2017 / 16:10:10 / cg"
! !

!QDouble class methodsFor:'constants'!

e
    "return the constant e as quad precision double.
     (returns approx. 212 bits of precision)"

    E isNil ifTrue:[
        E := self fromDoubleArray(DoubleArray 
                                    with: 2.718281828459045091e+00
                                    with: 1.445646891729250158e-16
                                    with: -2.127717108038176765e-33
                                    with: 1.515630159841218954e-49)
    ].    
    ^ E

    "
     self e
    "

    "Created: / 12-06-2017 / 18:29:36 / cg"
!

ln10
    "return the constant e as quad precision double.
     (returns approx. 212 bits of precision)"

    Ln10 isNil ifTrue:[
        Ln10 := self fromDoubleArray(DoubleArray 
                                    with: 2.302585092994045901e+00
                                    with: -2.170756223382249351e-16
                                    with: -9.984262454465776570e-33
                                    with: -4.023357454450206379e-49)
    ].    
    ^ Ln10

    "
     self ln10
    "

    "Created: / 12-06-2017 / 18:32:29 / cg"
!

ln2
    "return the constant e as quad precision double.
     (returns approx. 212 bits of precision)"

    Ln2 isNil ifTrue:[
        Ln2 := self fromDoubleArray(DoubleArray 
                                    with: 6.931471805599452862e-01
                                    with: 2.319046813846299558e-17
                                    with: 5.707708438416212066e-34
                                    with: -3.582432210601811423e-50)
    ].    
    ^ Ln2

    "
     self ln2
    "

    "Created: / 12-06-2017 / 18:31:34 / cg"
!

pi
    "return the constant pi as quad precision double.
     (returns approx. 212 bits of precision)"

    Pi isNil ifTrue:[
        Pi := self fromDoubleArray(DoubleArray 
                                    with: 3.141592653589793116e+00
                                    with: 1.224646799147353207e-16
                                    with: -2.994769809718339666e-33
                                    with: 1.112454220863365282e-49)
    ].    
    ^ Pi

    "
     self pi
    "

    "Created: / 12-06-2017 / 18:27:13 / cg"
! !

!QDouble class methodsFor:'queries'!

epsilon
    "return the maximum relative spacing of instances of mySelf
     (i.e. the value-delta of the least significant bit)"

    ^ Float epsilon

    "
     Float epsilon
     ShortFloat epsilon
     QDouble epsilon
    "

    "Created: / 12-06-2017 / 18:52:44 / cg"
!

numBitsInExponent
    "answer the number of bits in the exponent"

    ^ Float numBitsInExponent

    "
     1.0 asQDouble class numBitsInExponent
    "

    "Created: / 12-06-2017 / 11:11:04 / cg"
!

numBitsInMantissa
    "answer the number of bits in the mantissa"

    ^ Float precision * 4
    
    "
     1.0 class numBitsInMantissa
     1.0 asShortFloat class numBitsInMantissa
     1.0 asLongFloat class numBitsInMantissa
     1.0 asDDouble class numBitsInMantissa
     1.0 asQDouble class numBitsInMantissa

     Float numBitsInMantissa
     ShortFloat numBitsInMantissa
     QDouble numBitsInMantissa
    "

    "Created: / 12-06-2017 / 11:13:44 / cg"
    "Modified: / 12-06-2017 / 18:48:48 / cg"
!

precision
    "answer the number of bits in the mantissa"

    ^ Float precision * 4
    
    "
     1.0 class numBitsInMantissa
     1.0 asShortFloat class numBitsInMantissa
     1.0 asLongFloat class numBitsInMantissa
     1.0 asDDouble class numBitsInMantissa
     1.0 asQDouble class numBitsInMantissa

     Float numBitsInMantissa
     ShortFloat numBitsInMantissa
     QDouble numBitsInMantissa
     QDouble precision
    "

    "Created: / 12-06-2017 / 18:49:11 / cg"
!

radix
    "answer the radix of a Floats exponent
     This is an IEEE float, which is represented as binary"

    ^ Float radix

    "Created: / 12-06-2017 / 18:50:04 / cg"
! !

!QDouble methodsFor:'arithmetic'!

* aNumber
    "return the product of the receiver and the argument, aNumber"

    ^ aNumber productFromQDouble:self

    "
     ((QDouble fromFloat:1e20) + (QDouble fromFloat:1.0)) asDoubleArray
    "

    "Created: / 13-06-2017 / 01:00:47 / cg"
!

+ aNumber
    "return the sum of the receiver and the argument, aNumber"

    ^ aNumber sumFromQDouble:self

    "
     ((QDouble fromFloat:1e20) + (QDouble fromFloat:1.0)) asDoubleArray
    "

    "Created: / 12-06-2017 / 16:17:46 / cg"
    "Modified: / 12-06-2017 / 23:06:22 / cg"
!

- aNumber
    "return the sum of the receiver and the argument, aNumber"

    ^ (aNumber negated) sumFromQDouble:self

    "
     (QDouble fromFloat:1e20) asDoubleArray
     ((QDouble fromFloat:1e20) - (QDouble fromFloat:1.0)) asDoubleArray
     (QDouble fromFloat:1e-20) asDoubleArray
     ((QDouble fromFloat:1e-20) - (QDouble fromFloat:1.0)) asDoubleArray
     ((QDouble fromFloat:2.0) - (QDouble fromFloat:1.0)) asDoubleArray
    "

    "Created: / 12-06-2017 / 23:41:39 / cg"
!

/ aNumber
    "return the quotient of the receiver and the argument, aNumber"

    ^ aNumber quotientFromQDouble:self

    "
     ((QDouble fromFloat:1e20) / (QDouble fromFloat:2.0)) asDoubleArray
    "

    "Created: / 13-06-2017 / 17:59:09 / cg"
! !

!QDouble methodsFor:'coercing & converting'!

asDoubleArray
    ^ DoubleArray 
            with:self d0 
            with:self d1 
            with:self d2 
            with:self d3.

    "
     (QDouble fromFloat:1.0) asDoubleArray
     (QDouble fromFloat:2.0) asDoubleArray
    "

    "Created: / 12-06-2017 / 18:19:19 / cg"
    "Modified (comment): / 13-06-2017 / 17:58:09 / cg"
!

asFloat
    ^ self d0

    "
     (QDouble fromFloat:1.0) asFloat
     (QDouble fromFloat:2.0) asFloat
    "

    "Created: / 12-06-2017 / 18:15:27 / cg"
    "Modified: / 13-06-2017 / 17:56:50 / cg"
!

coerce:aNumber
    "convert the argument aNumber into an instance of the receiver's class and return it."

    ^ aNumber asQDouble

    "Created: / 12-06-2017 / 17:13:47 / cg"
    "Modified: / 12-06-2017 / 21:09:06 / cg"
!

generality
    "return the generality value - see ArithmeticValue>>retry:coercing:"

    ^ 95

    "Created: / 12-06-2017 / 17:13:14 / cg"
!

negative
    ^ self d0 negative

    "
     (QDouble fromFloat:0.0) negative
     (QDouble fromFloat:1.0) negative
     (QDouble fromFloat:-1.0) negative
    "

    "Created: / 13-06-2017 / 01:57:39 / cg"
    "Modified: / 13-06-2017 / 17:58:26 / cg"
!

positive
    ^ self d0 positive

    "
     (QDouble fromFloat:1.0) positive
     (QDouble fromFloat:-1.0) positive
    "

    "Created: / 13-06-2017 / 01:56:53 / cg"
    "Modified: / 13-06-2017 / 17:58:41 / cg"
! !

!QDouble methodsFor:'comparing'!

< aNumber
    "return true, if the argument, aNumber is greater than the receiver"

    ^ aNumber lessFromQDouble:self

    "Created: / 13-06-2017 / 16:58:53 / cg"
!

= aNumber
    "return true, if the argument, aNumber has the same value as than the receiver"

    ^ aNumber equalFromQDouble:self

    "Created: / 13-06-2017 / 17:12:09 / cg"
! !

!QDouble methodsFor:'double dispatching'!

differenceFromFloat:aFloat
    "aFloat - self"
    
    ^ aFloat + (self negated)

    "
     1.0 - (QDouble fromFloat:1.0)
     1e20 - (QDouble fromFloat:1.0)
     (1.0 - (QDouble fromFloat:1.0)) asFloat
     (1e20 - (QDouble fromFloat:1.0)) asFloat

     (1.0 - (QDouble fromFloat:1.0)) asDoubleArray
     (1e20 - (QDouble fromFloat:1.0)) asDoubleArray
     (1e20 - (QDouble fromFloat:1.0) + 1e-20) asDoubleArray
    "

    "Created: / 12-06-2017 / 23:38:05 / cg"
!

differenceFromQDouble:aQDouble
    "aQDouble - self"
    
    ^ aQDouble + (self negated)

    "
     1.0 - (QDouble fromFloat:1.0)
     1e20 - (QDouble fromFloat:1.0)
     (1.0 - (QDouble fromFloat:1.0)) asFloat
     (1e20 - (QDouble fromFloat:1.0)) asFloat

     (1.0 - (QDouble fromFloat:1.0)) asDoubleArray
     (1e20 - (QDouble fromFloat:1.0)) asDoubleArray
     (1e20 - (QDouble fromFloat:1.0) + 1e-20) asDoubleArray
    "

    "Created: / 12-06-2017 / 23:38:19 / cg"
!

equalFromQDouble:aQDouble
%{
    if (__Class(aQDouble) == QDouble) {
        double *a = __QuadDoubleInstPtr(self)->d_quadDoubleValue;
        double *b = __QuadDoubleInstPtr(aQDouble)->d_quadDoubleValue;

        RETURN ((a[0] == b[0] 
                && a[1] == b[1] 
                && a[2] == b[2] 
                && a[3] == b[3]) ? true : false);
    }
%}.
    ^ (aQDouble d0 = self d0)
    and:[ (aQDouble d1 = self d1)
    and:[ (aQDouble d2 = self d2)
    and:[ (aQDouble d3 = self d3) ]]]
    
    "
     (QDouble fromFloat:1.0) = (QDouble fromFloat:1.0)
     (QDouble fromFloat:1.0) = 1.0
     1.0 = (QDouble fromFloat:1.0)
   "

    "Created: / 13-06-2017 / 03:01:19 / cg"
    "Modified: / 13-06-2017 / 18:01:52 / cg"
!

lessFromQDouble:aQDouble
    "sent when aQDouble does not know how to compare to the receiver..
     Return true if aQDouble < self"

%{
    if (__Class(aQDouble) == QDouble) {
        double *a = __QuadDoubleInstPtr(aQDouble)->d_quadDoubleValue;
        double *b = __QuadDoubleInstPtr(self)->d_quadDoubleValue;

        // now compare if a < b!
        RETURN 
            ((a[0] < b[0] || 
              (a[0] == b[0] && (a[1] < b[1] ||
                (a[1] == b[1] && (a[2] < b[2] ||
                  (a[2] == b[2] && a[3] < b[3])))))) ? true : false);
    }
%}.
    ^ super lessFromQDouble:aQDouble

    "
     (1.0 + 1e-40) > 1.0
     ((QDouble fromFloat:1.0) + (QDouble fromFloat:1e-40)) > (QDouble fromFloat:1.0)

     (QDouble fromFloat:1.0) > (QDouble fromFloat:1.0)
     (QDouble fromFloat:1.1) > (QDouble fromFloat:1.0)
     (QDouble fromFloat:1.0) > 1.0
     (QDouble fromFloat:1.1) > 1.0
     1.0 > (QDouble fromFloat:1.0)
   "

    "Created: / 13-06-2017 / 17:07:47 / cg"
!

productFromFloat:aFloat
%{
    if (__isFloatLike(aFloat)) {
        double *a = __QuadDoubleInstPtr(self)->d_quadDoubleValue; 
        double b = __floatVal(aFloat);
        double p0, p1, p2, p3;
        double q0, q1, q2;
        double s0, s1, s2, s3, s4;
        OBJ newQD;
        int savedCV;
        
        fpu_fix_start(&savedCV);
        
        m_two_prod(p0, a[0], b, q0);
        m_two_prod(p1, a[1], b, q1);
        m_two_prod(p2, a[2], b, q2);
        p3 = a[3] * b;

        s0 = p0;

        m_two_sum(s1, q0, p1, s2);

        m_three_sum(s2, q1, p2);
        
        m_three_sum2(q1, q2, p3);
        s3 = q1;

        s4 = q2 + p2;

        m_renorm5(s0, s1, s2, s3, s4);
        fpu_fix_end(&savedCV);

        __qNew_qdReal(newQD, s0, s1, s2, s3);
        RETURN( newQD );
    }
%}.
    ^ super productFromFloat:aFloat.

    "
     (QDouble fromFloat:1.0) productFromFloat:2.0
     ((QDouble fromFloat:1e20) + (QDouble fromFloat:1.0)) productFromFloat:2.0
     ((QDouble fromFloat:1e20) + (QDouble fromFloat:1.0)) productFromFloat:2e20

     2.0 * (QDouble fromFloat:1.0)
     2.0 * (QDouble fromFloat:3.0)
     
     2.0 * ((QDouble fromFloat:1e20) + (QDouble fromFloat:1.0)) 
     (2.0 * ((QDouble fromFloat:1e20) + (QDouble fromFloat:1.0))) - (QDouble fromFloat:1e20) - (QDouble fromFloat:1e20)

     (2.0 * (QDouble fromFloat:1.0)) asFloat
     (1e20 * (QDouble fromFloat:1.0)) asFloat

     (1e20 * (QDouble fromFloat:1.0) * 1e-20) asDoubleArray
    "

    "Created: / 13-06-2017 / 00:58:56 / cg"
    "Modified: / 14-06-2017 / 11:42:57 / cg"
!

productFromQDouble:aQDouble
%{
    if (__Class(aQDouble) == QDouble) {
        double *a = __QuadDoubleInstPtr(self)->d_quadDoubleValue;
        double *b = __QuadDoubleInstPtr(aQDouble)->d_quadDoubleValue;
        OBJ newQD;

        // sloppy        
        double p0, p1, p2, p3, p4, p5;
        double q0, q1, q2, q3, q4, q5;
        double t0, t1;
        double s0, s1, s2;
        int savedCV;
        
        fpu_fix_start(&savedCV);

        m_two_prod(p0, a[0], b[0], q0);
fprintf(stderr, "%f * %f -> %f, %f\n", a[0], b[0], p0, q0);

        m_two_prod(p1, a[0], b[1], q1);
        m_two_prod(p2, a[1], b[0], q2);
fprintf(stderr, "%f * %f -> %f, %f\n", a[0], b[1], p1, q1);
fprintf(stderr, "%f * %f -> %f, %f\n", a[1], b[0], p2, q2);

        m_two_prod(p3, a[0], b[2], q3);
        m_two_prod(p4, a[1], b[1], q4);
        m_two_prod(p5, a[2], b[0], q5);

fprintf(stderr, "%f * %f -> %f, %f\n", a[0], b[2], p3, q3);
fprintf(stderr, "%f * %f -> %f, %f\n", a[1], b[1], p4, q4);
fprintf(stderr, "%f * %f -> %f, %f\n", a[2], b[0], p5, q5);

        /* Start Accumulation */
        m_three_sum(p1, p2, q0);

        /* Six-Three Sum  of p2, q1, q2, p3, p4, p5. */
        m_three_sum(p2, q1, q2);
        m_three_sum(p3, p4, p5);
        /* compute (s0, s1, s2) = (p2, q1, q2) + (p3, p4, p5). */
        m_two_sum(s0, p2, p3, t0);
        m_two_sum(s1, q1, p4, t1);
        s2 = q2 + p5;
        m_two_sum(s1, s1, t0, t0);
        s2 += (t0 + t1);

        /* O(eps^3) order terms */
        s1 += a[0]*b[3] + a[1]*b[2] + a[2]*b[1] + a[3]*b[0] + q0 + q3 + q4 + q5;
        m_renorm5(p0, p1, s0, s1, s2);

        fpu_fix_end(&savedCV);

        __qNew_qdReal(newQD, p0, p1, s0, s1);
        RETURN( newQD );
    }
%}.
    ^ super productFromQDouble:aQDouble.

    "
     (QDouble fromFloat:1.0) * 2.0
     2.0 * (QDouble fromFloat:1.0)
     (QDouble fromFloat:1.0) * (QDouble fromFloat:2.0)

     1e20 * (QDouble fromFloat:1.0)
     (2.0 * (QDouble fromFloat:1.0)) asFloat
     (1e20 * (QDouble fromFloat:1.0)) asFloat

     (1e20 * (QDouble fromFloat:1.0) * 1e-20) asDoubleArray
    "

    "Created: / 13-06-2017 / 01:06:22 / cg"
    "Modified: / 14-06-2017 / 11:43:28 / cg"
!

quotientFromQDouble:aQDouble
    "sloppy"
    
    |q0 q1 q2 q3 r|

    q0 := self d0 / aQDouble d0.
    r := self - (aQDouble * q0).

    q1 := r d0 / aQDouble d0.
    r := r - (aQDouble * q1).
    
    q2 := r d0 / aQDouble d0.
    r := r - (aQDouble * q2).

    q3 := r d0 / aQDouble d0.

    r := self class d0:q0 d1:q1 d2:q2 d3:q3.
    r renorm.
    ^ r

    "
     2.0 / (QDouble fromFloat:2.0)
     2.0 / (QDouble fromFloat:1.0)
     1e20 / (QDouble fromFloat:1.0)
     (2.0 / (QDouble fromFloat:1.0)) asFloat
     (1e20 / (QDouble fromFloat:1.0)) asFloat

     (QDouble fromFloat:2.0) / 2.0
     (QDouble fromFloat:1e20) / 2.0
     ((QDouble fromFloat:1.0) / 2.0) asFloat
     ((QDouble fromFloat:1e20 / 2.0)) asFloat
     
     ((1e20 + (QDouble fromFloat:1.0) + 1e-20) / 2.0) asDoubleArray
    "

    "Created: / 13-06-2017 / 17:50:35 / cg"
!

sumFromFloat:aFloat
%{
    if (__isFloatLike(aFloat)) {
        double *a = __QuadDoubleInstPtr(self)->d_quadDoubleValue; 
        double b = __floatVal(aFloat);
        double c0, c1, c2, c3;
        double e;
        OBJ newQD;
        int savedCV;
        
        fpu_fix_start(&savedCV);

        m_two_sum(c0, a[0], b, e);
        m_two_sum(c1 ,a[1], e, e);
        m_two_sum(c2, a[2], e, e);
        m_two_sum(c3, a[3], e, e);

        m_renorm5(c0, c1, c2, c3, e);
        fpu_fix_end(&savedCV);
        __qNew_qdReal(newQD, c0, c1, c2, c3);
        RETURN( newQD );
    }
%}.
    ^ super sumFromFloat:aFloat.

    "
     1.0 + (QDouble fromFloat:1.0)
     1e20 + (QDouble fromFloat:1.0)
     (1.0 + (QDouble fromFloat:1.0)) asFloat
     (1e20 + (QDouble fromFloat:1.0)) asFloat

     (1.0 + (QDouble fromFloat:1.0)) asDoubleArray
     (1e20 + (QDouble fromFloat:1.0)) asDoubleArray
     (1e20 + (QDouble fromFloat:1.0) + 1e-20) asDoubleArray
    "

    "Created: / 12-06-2017 / 17:16:41 / cg"
    "Modified: / 14-06-2017 / 11:43:47 / cg"
!

sumFromQDouble:aQDouble
%{
    if (__Class(aQDouble) == QDouble) {
        double *a = __QuadDoubleInstPtr(self)->d_quadDoubleValue;
        double *b = __QuadDoubleInstPtr(aQDouble)->d_quadDoubleValue;
        OBJ newQD;
        
#ifndef QD_IEEE_ADD
        // sloppy_add...

        /*
        double s0, s1, s2, s3;
        double t0, t1, t2, t3;
        int savedCV;

        fpu_fix_start(&savedCV);
        m_two_sum(s0, a[0], b[0], t0);
        m_two_sum(s1, a[1], b[1], t1);
        m_two_sum(s2, a[2], b[2], t2);
        m_two_sum(s3, a[3], b[3], t3);

        m_two_sum(s1, s1, t0, t0);
        m_three_sum(s2, t0, t1);
        m_three_sum2(s3, t0, t2);
        t0 = t0 + t1 + t3;

        fpu_fix_end(&savedCV);
        m_renorm5(s0, s1, s2, s3, t0);
        return qd_real(s0, s1, s2, s3, t0);
        */

        /* Same as above, but addition re-organized to minimize
           data dependency ... unfortunately some compilers are
           not very smart to do this automatically */
        double s0, s1, s2, s3;
        double t0, t1, t2, t3;
        double v0, v1, v2, v3;
        double u0, u1, u2, u3;
        double w0, w1, w2, w3;
        int savedCV;
        
        fpu_fix_start(&savedCV);
        s0 = a[0] + b[0];
        s1 = a[1] + b[1];
        s2 = a[2] + b[2];
        s3 = a[3] + b[3];

        v0 = s0 - a[0];
        v1 = s1 - a[1];
        v2 = s2 - a[2];
        v3 = s3 - a[3];

        u0 = s0 - v0;
        u1 = s1 - v1;
        u2 = s2 - v2;
        u3 = s3 - v3;

        w0 = a[0] - u0;
        w1 = a[1] - u1;
        w2 = a[2] - u2;
        w3 = a[3] - u3;

        u0 = b[0] - v0;
        u1 = b[1] - v1;
        u2 = b[2] - v2;
        u3 = b[3] - v3;

        t0 = w0 + u0;
        t1 = w1 + u1;
        t2 = w2 + u2;
        t3 = w3 + u3;

        m_two_sum(s1, s1, t0, t0);
        m_three_sum(s2, t0, t1);
        m_three_sum2(s3, t0, t2);
        t0 = t0 + t1 + t3;

        /* renormalize */
        m_renorm5(s0, s1, s2, s3, t0);
        fpu_fix_end(&savedCV);
        __qNew_qdReal(newQD, s0, s1, s2, s3);
        RETURN(newQD);                 
#else
        // ieee_add...
        int i, j, k;
        double s, t;
        double u, v;   /* double-length accumulator */
        double x[4] = {0.0, 0.0, 0.0, 0.0};
        int savedCV;
        
        fpu_fix_start(&savedCV);
        i = j = k = 0;
        if (abs(a[i]) > abs(b[j]))
          u = a[i++];
        else
          u = b[j++];
        if (abs(a[i]) > abs(b[j]))
          v = a[i++];
        else
          v = b[j++];

        u = quick_two_sum(u, v, v);

        while (k < 4) {
          if (i >= 4 && j >= 4) {
            x[k] = u;
            if (k < 3)
              x[++k] = v;
            break;
          }

          if (i >= 4)
            t = b[j++];
          else if (j >= 4)
            t = a[i++];
          else if (abs(a[i]) > abs(b[j])) {
            t = a[i++];
          } else
            t = b[j++];

          s = quick_three_accum(u, v, t);

          if (s != 0.0) {
            x[k++] = s;
          }
        }

        /* add the rest. */
        for (k = i; k < 4; k++)
          x[3] += a[k];
        for (k = j; k < 4; k++)
          x[3] += b[k];

        m_renorm4(x[0], x[1], x[2], x[3]);
        fpu_fix_end(&savedCV);
        __qNew_qdReal(newQD, x[0], x[1], x[2], x[3]);
        RETURN(newQD);                 
#endif
    }
%}.
    ^ super sumFromQDouble:aQDouble
    
    "
     (QDouble fromFloat:1.0) + (QDouble fromFloat:1.0)
     (QDouble fromFloat:1.0) + 1.0
     1.0 + (QDouble fromFloat:1.0)

     ((QDouble fromFloat:1.0) + (QDouble fromFloat:1.0)) asDoubleArray
     ((QDouble fromFloat:1.0) + 1.0) asDoubleArray
     (1.0 + (QDouble fromFloat:1.0)) asDoubleArray
     (1e-20 + (QDouble fromFloat:1.0)) asDoubleArray
     (1e20 + (QDouble fromFloat:1.0)) asDoubleArray
   "

    "Created: / 12-06-2017 / 21:15:43 / cg"
    "Modified: / 14-06-2017 / 11:44:53 / cg"
! !

!QDouble methodsFor:'inspecting'!

inspectorExtraAttributes
    "extra (pseudo instvar) entries to be shown in an inspector."

    ^ super inspectorExtraAttributes
        add:'-{doubles}' ->
            [
                self asDoubleArray printString
            ];
        yourself

    "Created: / 12-06-2017 / 23:43:05 / cg"
! !

!QDouble methodsFor:'mathematical functions'!

floor
    "return the receiver truncated towards negative infinity"

%{
    double *a = __QuadDoubleInstPtr(self)->d_quadDoubleValue;
    OBJ newQD;

    double x0, x1, x2, x3;
    x1 = x2 = x3 = 0.0;
    x0 =floor(a[0]);

    if (x0 == a[0]) {
        x1 = floor(a[1]);

        if (x1 == a[1]) {
            x2 = floor(a[2]);

            if (x2 == a[2]) {
                x3 = floor(a[3]);
            }
        }

        m_renorm4(x0, x1, x2, x3);
        __qNew_qdReal(newQD, x0, x1, x2, x3);
        RETURN( newQD );
    }

    __qNew_qdReal(newQD, x0, x1, x2, x3);
    RETURN( newQD );
%}.

    "
     (QDouble fromFloat:4.0) floor
     (QDouble fromFloat:4.1) floor
     (QDouble fromFloat:0.1) floor
     (0.1 + (QDouble fromFloat:1.0)) floor
     (1e20 + (QDouble fromFloat:1.0)) floor 

     (QDouble fromFloat:1.5) floor
     (QDouble fromFloat:0.5) floor
     (QDouble fromFloat:-0.5) floor
     (QDouble fromFloat:-1.5) floor
    "

    "Created: / 13-06-2017 / 01:52:44 / cg"
    "Modified (comment): / 13-06-2017 / 17:33:19 / cg"
!

negated
    ^ self class 
        d0:(self d0) negated 
        d1:(self d1) negated 
        d2:(self d2) negated 
        d3:(self d3) negated

    "    
     (QDouble fromFloat:1.0) negated
     ((QDouble fromFloat:1e20) + (QDouble fromFloat:1.0)) negated asDoubleArray

     (((QDouble fromFloat:1e20) + (QDouble fromFloat:1.0))
     + ((QDouble fromFloat:1e20) + (QDouble fromFloat:1.0))) asDoubleArray
    "

    "Created: / 12-06-2017 / 20:14:55 / cg"
    "Modified (comment): / 12-06-2017 / 23:46:57 / cg"
!

squared
%{
    double *a = __QuadDoubleInstPtr(self)->d_quadDoubleValue;
    double p0, p1, p2, p3, p4, p5;
    double q0, q1, q2, q3;
    double s0, s1;
    double t0, t1;
    double t;
    OBJ newQD;
    
    m_two_sqr(p0, a[0], q0);
    t = 2.0 * a[0];

    m_two_prod(p1, t, a[1], q1);
    m_two_prod(p2, t, a[2], q2);
    m_two_sqr(p3, a[1], q3);

    m_two_sum(p1, q0, p1, q0);

    m_two_sum(q0, q0, q1, q1);
    m_two_sum(p2, p2, p3, p3);

    m_two_sum(s0, q0, p2, t0);
    m_two_sum(s1, q1, p3, t1);

    m_two_sum(s1, s1, t0, t0);
    t0 += t1;

    m_quick_two_sum(s1, s1, t0, t0);
    m_quick_two_sum(p2, s0, s1, t1);
    m_quick_two_sum(p3, t1, t0, q0);

    p4 = 2.0 * a[0] * a[3];
    p5 = 2.0 * a[1] * a[2];

    m_two_sum(p4, p4, p5, p5);
    m_two_sum(q2, q2, q3, q3);

    m_two_sum(t0, p4, q2, t1);
    t1 = t1 + p5 + q3;

    m_two_sum(p3, p3, t0, p4);
    p4 = p4 + q0 + t1;

    m_renorm5(p0, p1, p2, p3, p4);

    __qNew_qdReal(newQD, p0, p1, s0, s1);
    RETURN( newQD );
%}.

    "
     (QDouble fromFloat:4.0) squared
     (1e20 + (QDouble fromFloat:1.0)) squared 
    "

    "Created: / 13-06-2017 / 01:27:58 / cg"
! !

!QDouble methodsFor:'printing & storing'!

printDigitsOn:outStream precision:precision exponentInto:expn
    |numDigits r e i d|

    numDigits := precision+1. "/ number of digits
    r := self abs.
    self d0 = 0.0 ifTrue:[
        expn value:0.
        precision timesRepeat:[ outStream nextPut:$0 ].
        ^ self.
    ].

    "/ determine approx. exponent
    e := self d0 abs log10 floor.
    self halt.
    e < -300 ifTrue:[
        r := r * (10.0 raisedToInteger:300).
        r := r / (10.0 raisedToInteger:(e+300)).
    ] ifFalse:[
        e > 300 ifTrue:[
        ] ifFalse:[
        ]
    ].
    
    
    "
     1.2345 asQDouble printDigitsOn:Transcript precision:5 exponentInto:[:e |]
    "

    "Created: / 13-06-2017 / 17:28:08 / cg"
!

printString
    "return a printed representation of the receiver"

    self d1 = 0.0 ifTrue:[
        ^ self d0 printString
    ].
    ^ self d0 printString , '...'

    "Created: / 12-06-2017 / 23:41:04 / cg"
! !

!QDouble methodsFor:'private accessing'!

d0
    "the most significant (and highest valued) 53 bits of precision"
%{
    RETURN ( __MKFLOAT(__QuadDoubleInstPtr(self)->d_quadDoubleValue[0]) ); 
%}

    "Created: / 12-06-2017 / 20:15:12 / cg"
    "Modified (comment): / 13-06-2017 / 17:59:47 / cg"
!

d1
    "the next most significant (and next highest valued) 53 bits of precision"
%{
    RETURN ( __MKFLOAT(__QuadDoubleInstPtr(self)->d_quadDoubleValue[1]) ); 
%}

    "Created: / 12-06-2017 / 20:15:12 / cg"
    "Modified (comment): / 13-06-2017 / 18:00:00 / cg"
!

d2
%{
    RETURN ( __MKFLOAT(__QuadDoubleInstPtr(self)->d_quadDoubleValue[2]) ); 
%}

    "Created: / 12-06-2017 / 20:15:29 / cg"
!

d3
    "the least significant (and smallest valued) 53 bits of precision"
%{
    RETURN ( __MKFLOAT(__QuadDoubleInstPtr(self)->d_quadDoubleValue[3]) ); 
%}

    "Created: / 12-06-2017 / 20:15:32 / cg"
    "Modified (comment): / 13-06-2017 / 18:00:18 / cg"
!

renorm
%{
    double *a = __QuadDoubleInstPtr(self)->d_quadDoubleValue; 
    double c0, c1, c2, c3;
    OBJ newQD;

    c0 = a[0];
    c1 = a[1];
    c2 = a[2];
    c3 = a[3];
    m_renorm4(c0, c1, c2, c3);
    a[0] = c0;
    a[1] = c1;
    a[2] = c2;
    a[3] = c3;
    RETURN( self );
%}.
    ^ self error.

    "
     (QDouble fromFloat:1.0) renorm
    "

    "Created: / 13-06-2017 / 18:05:33 / cg"
! !

!QDouble class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
! !