class: ShortFloat
authorStefan Vogel <sv@exept.de>
Mon, 30 Jun 2014 21:43:16 +0200
changeset 16670 b81b77ec431d
parent 16669 8488266c39e5
child 16671 1e5a9d90ea7b
class: ShortFloat class definition added:5 methods changed: #defaultPrintFormat #printString #storeString
ShortFloat.st
--- a/ShortFloat.st	Mon Jun 30 21:40:12 2014 +0200
+++ b/ShortFloat.st	Mon Jun 30 21:43:16 2014 +0200
@@ -13,7 +13,7 @@
 
 LimitedPrecisionReal variableByteSubclass:#ShortFloat
 	instanceVariableNames:''
-	classVariableNames:''
+	classVariableNames:'DefaultPrintFormat Epsilon'
 	poolDictionaries:''
 	category:'Magnitude-Numbers'
 !
@@ -32,6 +32,10 @@
 #define __USE_ISOC99 1
 #include <math.h>
 
+#ifndef INT32
+# define INT32 int
+#endif
+
 /*
  * on some systems errno is a macro ... check for it here
  */
@@ -68,26 +72,26 @@
  */
 # ifndef isnan
 #  define isnan(x)      \
-	((((unsigned int *)(&x))[0] == 0x00000000) && \
-	 (((unsigned int *)(&x))[1] == 0xFFF80000))
+        ((((unsigned int *)(&x))[0] == 0x00000000) && \
+         (((unsigned int *)(&x))[1] == 0xFFF80000))
 # endif
 
 # ifndef isPositiveInfinity
 #  define isPositiveInfinity(x) \
-	((((unsigned int *)(&x))[0] == 0x00000000) && \
-	 (((unsigned int *)(&x))[1] == 0x7FF00000))
+        ((((unsigned int *)(&x))[0] == 0x00000000) && \
+         (((unsigned int *)(&x))[1] == 0x7FF00000))
 # endif
 
 # ifndef isNegativeInfinity
 #  define isNegativeInfinity(x) \
-	((((unsigned int *)(&x))[0] == 0x00000000) && \
-	 (((unsigned int *)(&x))[1] == 0xFFF00000))
+        ((((unsigned int *)(&x))[0] == 0x00000000) && \
+         (((unsigned int *)(&x))[1] == 0xFFF00000))
 # endif
 
 # ifndef isinf
 #  define isinf(x) \
-	((((unsigned int *)(&x))[0] == 0x00000000) && \
-	 ((((unsigned int *)(&x))[1] & 0x7FF00000) == 0x7FF00000))
+        ((((unsigned int *)(&x))[0] == 0x00000000) && \
+         ((((unsigned int *)(&x))[1] & 0x7FF00000) == 0x7FF00000))
 # endif
 
 # ifndef isfinite
@@ -96,22 +100,22 @@
 
 # ifndef isnanf
 #  define isnanf(x)      \
-	(((unsigned int *)(&x))[0] == 0xFFC00000)
+        (((unsigned int *)(&x))[0] == 0xFFC00000)
 # endif
 
 # ifndef isPositiveInfinityf
 #  define isPositiveInfinityf(x)      \
-	(((unsigned int *)(&x))[0] == 0x7F800000)
+        (((unsigned int *)(&x))[0] == 0x7F800000)
 # endif
 
 # ifndef isNegativeInfinityf
 #  define isNegativeInfinityf(x)      \
-	(((unsigned int *)(&x))[0] == 0xFF800000)
+        (((unsigned int *)(&x))[0] == 0xFF800000)
 # endif
 
 # ifndef isinff
 #  define isinff(x)      \
-	((((unsigned int *)(&x))[0] & 0x7FFFFFFF) == 0x7F800000)
+        ((((unsigned int *)(&x))[0] & 0x7FFFFFFF) == 0x7F800000)
 # endif
 
 # ifndef isfinitef
@@ -316,12 +320,17 @@
 !ShortFloat class methodsFor:'accessing'!
 
 defaultPrintFormat
-    "this is fixed in ShortFloat"
+    ^ DefaultPrintFormat
+!
 
-    ^ '.6'
+defaultPrintFormat:aString
+    DefaultPrintFormat := aString.
+!
+
+epsilon
+    ^ Epsilon
 ! !
 
-
 !ShortFloat class methodsFor:'binary storage'!
 
 readBinaryIEEESingleFrom:aStream
@@ -438,6 +447,20 @@
     "Modified: / 23-08-2006 / 16:01:55 / cg"
 ! !
 
+!ShortFloat class methodsFor:'class initialization'!
+
+initialize
+    Epsilon isNil ifTrue:[
+        DefaultPrintFormat := '.7'.  "/ print 7 valid digits
+        Epsilon := self computeEpsilon.
+    ].
+
+    "
+     Epsilon := nil.
+     self initialize
+    "
+! !
+
 !ShortFloat class methodsFor:'constants'!
 
 e
@@ -997,6 +1020,92 @@
     "
 !
 
+isAlmostEqualTo:aNumber nEpsilon:nE 
+    "return true, if the argument, aNumber represents almost the same numeric value
+     as the receiver, false otherwise.
+
+     nE is the number of minimal float distances, that the numbers may differ and
+     still be considered equal.
+
+     For background information why floats need this 
+     read: http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
+    "
+
+%{  /* NOCONTEXT */
+
+    /*
+     * notice:
+     * the following inline code handles some common cases,
+     * and exists as an optimization, to speed up those cases.
+     *
+     * Conceptionally, (and for most other argument types),
+     * mixed arithmetic is implemented by double dispatching
+     * (see the message send at the bottom)
+     */
+
+    INT32 ulpDiff;
+    union {
+        float f;
+        INT32 i;
+    } myself, otherFloat;
+    int nEpsilon;
+    float scaledEpsilon;
+
+
+    if (!__isSmallInteger(nE)) {
+        goto tryHarder;
+    }
+
+    nEpsilon =  __intVal(nE);
+    scaledEpsilon = nEpsilon *__shortFloatVal(@global(Epsilon));
+
+    if (__isSmallInteger(aNumber)) {
+        otherFloat.f = (float)(__intVal(aNumber));
+    } else if (aNumber == nil) {
+        RETURN(false)
+    } else if (__qIsFloatLike(aNumber)) {
+        otherFloat.f = (float)(__floatVal(aNumber));
+    } else if (__qIsShortFloat(aNumber)) {
+        otherFloat.f = (double)(__shortFloatVal(aNumber));
+    } else {
+        goto tryHarder;
+    }
+
+    myself.f = __shortFloatVal(self);
+
+    // Check if the numbers are really close -- needed
+    // when comparing numbers near zero (ULP method below fails for numbers near 0!).
+    if (fabs(myself.f - otherFloat.f) <= scaledEpsilon) {
+        RETURN(true);
+    }
+
+    // if the signs differ, the numbers are different
+    if ((myself.f >= 0) != (otherFloat.f >= 0)) {
+        RETURN(false);
+    }
+
+    // compute the difference of the 'units in the last place" ULP
+    // (if ulpDiff == 1, two floats are adjecant)
+    ulpDiff = myself.i - otherFloat.i;
+    if (ulpDiff < 0) ulpDiff = -ulpDiff;
+    if (ulpDiff <= nEpsilon) {
+        RETURN(true);
+    } else {
+        RETURN(false)
+    }
+
+tryHarder:;
+%}.
+    ^ aNumber isAlmostEqualToFromShortFloat:self nEpsilon:nE
+
+    "
+        67329.234 asShortFloat isAlmostEqualTo:67329.23400000001 nEpsilon:1
+        1.0 asShortFloat isAlmostEqualTo:1.0001 nEpsilon:1
+        1.0 asShortFloat isAlmostEqualTo:-1.0 nEpsilon:1
+        1.0 asShortFloat isAlmostEqualTo:1 nEpsilon:1
+    "
+!
+
 ~= aNumber
     "return true, if the arguments value are not equal"
 
@@ -1092,6 +1201,24 @@
     REGISTER char *cp;
     int len;
     OBJ s;
+    char *fmt;
+    char fmtBuffer[20];
+
+    if (__isStringLike(@global(DefaultPrintFormat))) {
+        fmt = (char *) __stringVal(@global(DefaultPrintFormat));
+    } else {
+        /*
+         * in case we get called before #initialize ...
+         */
+        fmt = ".7";
+    }
+
+    /*
+     * build a printf format string
+     */
+    fmtBuffer[0] = '%';
+    strncpy(fmtBuffer+1, fmt, 10);
+    strcat(fmtBuffer, "g");
 
     /*
      * actually only needed on sparc: since thisContext is
@@ -1099,60 +1226,65 @@
      * manually save it here - very stupid ...
      */
     __BEGIN_PROTECT_REGISTERS__
-    len = snprintf(buffer, sizeof(buffer), "%.6g", (float)__shortFloatVal(self));
+    len = snprintf(buffer, sizeof(buffer), fmtBuffer, (float)__shortFloatVal(self));
     __END_PROTECT_REGISTERS__
 
     if (len >= 0 && len <= sizeof(buffer)-3) {
-	/*
-	 * kludge to make integral float f prints as "f.0" (not as "f" as printf does)
-	 * (i.e. look if string contains '.' or 'e' and append '.0' if not)
-	 */
-	for (cp = buffer; *cp; cp++) {
-	    if ((*cp == '.') || (*cp == ',') || (*cp == 'E') || (*cp == 'e')) break;
-	}
-	if (!*cp && (cp[-1] >= '0') && (cp[-1] <= '9')) {
-	    if (__isCharacter(@global(DecimalPointCharacterForPrinting))) {
-		*cp++ = __intVal(__characterVal(@global(DecimalPointCharacterForPrinting)));
-	    } else {
-		*cp++ = '.';
-	    }
-	    *cp++ = '0';
-	    *cp = '\0';
-	} else {
-	    if (cp && (*cp == '.')) {
-		if (__isCharacter(@global(DecimalPointCharacterForPrinting))) {
-		    *cp = __intVal(__characterVal(@global(DecimalPointCharacterForPrinting)));
-		}
-	    }
-	}
+        /*
+         * kludge to make integral float f prints as "f.0" (not as "f" as printf does)
+         * (i.e. look if string contains '.' or 'e' and append '.0' if not)
+         */
+        for (cp = buffer; *cp; cp++) {
+            if ((*cp == '.') || (*cp == ',') || (*cp == 'E') || (*cp == 'e')) break;
+        }
+        if (!*cp && (cp[-1] >= '0') && (cp[-1] <= '9')) {
+            if (__isCharacter(@global(DecimalPointCharacterForPrinting))) {
+                *cp++ = __intVal(__characterVal(@global(DecimalPointCharacterForPrinting)));
+            } else {
+                *cp++ = '.';
+            }
+            *cp++ = '0';
+            *cp = '\0';
+        } else {
+            if (cp && (*cp == '.')) {
+                if (__isCharacter(@global(DecimalPointCharacterForPrinting))) {
+                    *cp = __intVal(__characterVal(@global(DecimalPointCharacterForPrinting)));
+                }
+            }
+        }
 
-	s = __MKSTRING(buffer);
-	if (s != nil) {
-	    RETURN (s);
-	}
+        s = __MKSTRING(buffer);
+        if (s != nil) {
+            RETURN (s);
+        }
     }
 %}.
     ^ self asFloat printString
 
     "
-	1.234 asShortFloat printString.
-	1.0 asShortFloat printString.
-	1e10 asShortFloat printString.
-	1.2e3 asShortFloat printString.
-	1.2e30 asShortFloat printString.
-	(1.0 uncheckedDivide:0) asShortFloat printString.
-	(0.0 uncheckedDivide:0) asShortFloat printString.
-	self pi printString.
+        1.234 asShortFloat printString.
+        1.0 asShortFloat printString.
+        1e10 asShortFloat printString.
+        1.2e3 asShortFloat printString.
+        1.2e30 asShortFloat printString.
+        (1.0 uncheckedDivide:0) asShortFloat printString.
+        (0.0 uncheckedDivide:0) asShortFloat printString.
+        self pi printString.
 
-	DecimalPointCharacterForPrinting := $,.
-	1.234 asShortFloat printString.
-	1.0 asShortFloat printString.
-	1e10 asShortFloat printString.
-	1.2e3 asShortFloat printString.
-	1.2e30 asShortFloat printString.
-	(1.0 uncheckedDivide:0) asShortFloat printString.
-	(0.0 uncheckedDivide:0) asShortFloat printString.
-	DecimalPointCharacterForPrinting := $.
+        self pi printString.
+        DefaultPrintFormat := '.3'.
+        self pi printString.
+        DefaultPrintFormat := '.7'.
+
+        DecimalPointCharacterForPrinting := $,.
+        1.234 asShortFloat printString.
+        1.0 asShortFloat printString.
+        1e10 asShortFloat printString.
+        1.2e3 asShortFloat printString.
+        1.2e30 asShortFloat printString.
+        (1.0 uncheckedDivide:0) asShortFloat printString.
+        (0.0 uncheckedDivide:0) asShortFloat printString.
+        DecimalPointCharacterForPrinting := $.
     "
 !
 
@@ -1223,31 +1355,27 @@
      */
 
     __BEGIN_PROTECT_REGISTERS__
-#ifdef SYSV
-    len = snprintf(buffer, sizeof(buffer), "%.6lg", (double)__shortFloatVal(self));
-#else
-    len = snprintf(buffer, sizeof(buffer), "%.6G", (double)__shortFloatVal(self));
-#endif
+    len = snprintf(buffer, sizeof(buffer), "%.8g", (float)__shortFloatVal(self));
     __END_PROTECT_REGISTERS__
 
     if (len >= 0 && len < sizeof(buffer)-3) {
-	/*
-	 * kludge to make integral float f prints as "f.0" (not as "f" as printf does)
-	 * (i.e. look if string contains '.' or 'e' and append '.0' if not)
-	 */
-	for (cp = buffer; *cp; cp++) {
-	    if ((*cp == '.') || (*cp == ',') || (*cp == 'E') || (*cp == 'e')) break;
-	}
-	if (!*cp && (cp[-1] >= '0') && (cp[-1] <= '9')) {
-	    *cp++ = '.';
-	    *cp++ = '0';
-	    *cp = '\0';
-	}
+        /*
+         * kludge to make integral float f prints as "f.0" (not as "f" as printf does)
+         * (i.e. look if string contains '.' or 'e' and append '.0' if not)
+         */
+        for (cp = buffer; *cp; cp++) {
+            if ((*cp == '.') || (*cp == ',') || (*cp == 'E') || (*cp == 'e')) break;
+        }
+        if (!*cp && (cp[-1] >= '0') && (cp[-1] <= '9')) {
+            *cp++ = '.';
+            *cp++ = '0';
+            *cp = '\0';
+        }
 
-	s = __MKSTRING(buffer);
-	if (s != nil) {
-	    RETURN (s);
-	}
+        s = __MKSTRING(buffer);
+        if (s != nil) {
+            RETURN (s);
+        }
     }
 %}.
     "
@@ -1260,26 +1388,28 @@
     ^ ObjectMemory allocationFailureSignal raise.
 
     "
-	1.0 asShortFloat storeString
-	1.234 asShortFloat storeString
-	1e10 asShortFloat storeString
-	1.2e3 asShortFloat storeString
-	1.2e30 asShortFloat storeString
-	Float pi asShortFloat storeString
-	(1.0 uncheckedDivide:0) asShortFloat storeString
-	(0.0 uncheckedDivide:0) asShortFloat storeString
+        0.1 asShortFloat storeString
+        ((Array new:10 withAll:0.1 asShortFloat) inject:0 into:[:v :sumSoFar| sumSoFar + v]) storeString
+        1.0 asShortFloat storeString
+        1.234 asShortFloat storeString
+        1e10 asShortFloat storeString
+        1.2e3 asShortFloat storeString
+        1.2e30 asShortFloat storeString
+        Float pi asShortFloat storeString
+        (1.0 uncheckedDivide:0) asShortFloat storeString
+        (0.0 uncheckedDivide:0) asShortFloat storeString
 
      notice that the storeString is NOT affected by DecimalPointCharacterForPrinting:
 
-	DecimalPointCharacterForPrinting := $,.
-	1.234 asShortFloat storeString.
-	1.0 asShortFloat storeString.
-	1e10 asShortFloat storeString.
-	1.2e3 asShortFloat storeString.
-	1.2e30 asShortFloat storeString.
-	(1.0 uncheckedDivide:0) asShortFloat storeString.
-	(0.0 uncheckedDivide:0) asShortFloat storeString.
-	DecimalPointCharacterForPrinting := $.
+        DecimalPointCharacterForPrinting := $,.
+        1.234 asShortFloat storeString.
+        1.0 asShortFloat storeString.
+        1e10 asShortFloat storeString.
+        1.2e3 asShortFloat storeString.
+        1.2e30 asShortFloat storeString.
+        (1.0 uncheckedDivide:0) asShortFloat storeString.
+        (0.0 uncheckedDivide:0) asShortFloat storeString.
+        DecimalPointCharacterForPrinting := $.
     "
 ! !
 
@@ -1368,6 +1498,35 @@
     ^ self indexNotIntegerOrOutOfBounds:index
 ! !
 
+!ShortFloat methodsFor:'queries'!
+
+nextFloat:count
+    "answer the next float count places after (or before if count is negative) myself"
+
+%{
+    union u {
+        float d;
+        INT32 i;
+    } this;
+
+    if (__isSmallInteger(count)) {
+        this.d = __shortFloatVal(self);
+        if (isfinite(this.d))
+            this.i += __intVal(count);
+
+        RETURN(__MKSFLOAT(this.d));
+    }
+%}.
+    self primitiveFailed:#badArgument
+
+  "
+     (1.0 asShortFloat nextFloat:2) storeString
+     (67329.234 asShortFloat nextFloat:1) storeString
+     ShortFloat NaN nextFloat:100000
+     ShortFloat infinity nextFloat:100000
+  "
+! !
+
 !ShortFloat methodsFor:'special access'!
 
 exponent
@@ -1867,10 +2026,12 @@
 !ShortFloat class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic/ShortFloat.st,v 1.130 2014-06-25 12:20:41 stefan Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic/ShortFloat.st,v 1.131 2014-06-30 19:43:16 stefan Exp $'
 !
 
 version_CVS
-    ^ '$Header: /cvs/stx/stx/libbasic/ShortFloat.st,v 1.130 2014-06-25 12:20:41 stefan Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic/ShortFloat.st,v 1.131 2014-06-30 19:43:16 stefan Exp $'
 ! !
 
+
+ShortFloat initialize!