--- 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!