--- a/Float.st Mon Jun 30 16:15:35 2014 +0200
+++ b/Float.st Mon Jun 30 16:17:28 2014 +0200
@@ -14,7 +14,7 @@
LimitedPrecisionReal variableByteSubclass:#Float
instanceVariableNames:''
classVariableNames:'DefaultPrintFormat Pi E Halfpi HalfpiNegative Twopi
- RadiansPerDegree Ln2 Ln10 Sqrt2'
+ RadiansPerDegree Ln2 Ln10 Sqrt2 Epsilon'
poolDictionaries:''
category:'Magnitude-Numbers'
!
@@ -397,6 +397,10 @@
defaultPrintFormat:something
DefaultPrintFormat := something.
+!
+
+epsilon
+ ^ epsilon
! !
@@ -509,16 +513,17 @@
initialize
Pi isNil ifTrue:[
- DefaultPrintFormat := '.15'. "/ print 15 valid digits
- Pi := 3.14159265358979323846264338327950288419716939937510582097494459.
- Halfpi := Pi / 2.0.
- HalfpiNegative := Halfpi negated.
- Twopi := Pi * 2.0.
- E := 2.7182818284590452353602874713526625.
- Sqrt2 := 1.41421356237309504880168872420969808.
- RadiansPerDegree := Pi / 180.0.
- Ln2 := 0.69314718055994530941723212145817657.
- Ln10 := 10.0 ln.
+ DefaultPrintFormat := '.15'. "/ print 15 valid digits
+ Pi := 3.14159265358979323846264338327950288419716939937510582097494459.
+ Halfpi := Pi / 2.0.
+ HalfpiNegative := Halfpi negated.
+ Twopi := Pi * 2.0.
+ E := 2.7182818284590452353602874713526625.
+ Sqrt2 := 1.41421356237309504880168872420969808.
+ RadiansPerDegree := Pi / 180.0.
+ Ln2 := 0.69314718055994530941723212145817657.
+ Ln10 := 10.0 ln.
+ Epsilon := self computeEpsilon.
].
"
@@ -1376,6 +1381,98 @@
"
!
+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)
+ */
+
+#ifndef INT64
+# define INT64 long long int
+#endif
+ INT64 ulpDiff;
+ union {
+ double d;
+ INT64 i;
+ } myself, otherFloat;
+ int nEpsilon;
+ double scaledEpsilon;
+
+
+ if (!__isSmallInteger(nE)) {
+ goto tryHarder;
+ }
+
+ nEpsilon = __intVal(nE);
+ scaledEpsilon = nEpsilon *__floatVal(@global(Epsilon));
+
+ if (__isSmallInteger(aNumber)) {
+ otherFloat.d = (double)(__intVal(aNumber));
+ } else if (aNumber == nil) {
+ RETURN(false)
+ } else if (__qIsFloatLike(aNumber)) {
+ otherFloat.d = (double)(__floatVal(aNumber));
+ } else if (__qIsShortFloat(aNumber)) {
+ otherFloat.d = (double)(__shortFloatVal(aNumber));
+ } else {
+ goto tryHarder;
+ }
+
+ myself.d = __floatVal(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.d - otherFloat.d) <= scaledEpsilon) {
+ RETURN(true);
+ }
+
+ // if the signs differ, the numbers are different
+ if (signbit(myself.d) != signbit(otherFloat.d)) {
+ 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 isAlmostEqualToFromFloat:self nEpsilon:nE
+
+ "
+ 67329.234 isAlmostEqualTo:67329.23400000001 nEpsilon:1
+ 1.0 isAlmostEqualTo:1.0001 nEpsilon:1
+ 1.0 isAlmostEqualTo:-1.0 nEpsilon:1
+ 1 isAlmostEqualTo:1.000000000000001 nEpsilon:1
+ 1 isAlmostEqualTo:1.000000000000001 nEpsilon:10
+ 1.0 isAlmostEqualTo:1 nEpsilon:1
+ 1.0 isAlmostEqualTo:1 asFraction nEpsilon:1
+ "
+!
+
~= aNumber
"return true, if the arguments value are not equal"
@@ -1763,30 +1860,30 @@
__BEGIN_PROTECT_REGISTERS__
#ifdef SYSV
- len = snprintf(buffer, sizeof(buffer), "%.15lg", __floatVal(self));
+ len = snprintf(buffer, sizeof(buffer), "%.17lg", __floatVal(self));
#else
- len = snprintf(buffer, sizeof(buffer), "%.15G", __floatVal(self));
+ len = snprintf(buffer, sizeof(buffer), "%.17G", __floatVal(self));
#endif
__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';
- }
-
- s = __MKSTRING(buffer);
- if (s != nil) {
- RETURN (s);
- }
+ /*
+ * 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);
+ }
}
%}.
"
@@ -1799,26 +1896,26 @@
^ ObjectMemory allocationFailureSignal raise.
"
- 1.0 storeString
- 1.234 storeString
- 1e10 storeString
- 1.2e3 storeString
- 1.2e30 storeString
- Float pi storeString
- (1.0 uncheckedDivide:0) storeString
- (0.0 uncheckedDivide:0) storeString
+ 1.0 storeString
+ 1.234 storeString
+ 1e10 storeString
+ 1.2e3 storeString
+ 1.2e30 storeString
+ Float pi storeString
+ (1.0 uncheckedDivide:0) storeString
+ (0.0 uncheckedDivide:0) storeString
notice that the storeString is NOT affected by DecimalPointCharacterForPrinting:
- DecimalPointCharacterForPrinting := $,.
- 1.234 storeString.
- 1.0 storeString.
- 1e10 storeString.
- 1.2e3 storeString.
- 1.2e30 storeString.
- (1.0 uncheckedDivide:0) storeString.
- (0.0 uncheckedDivide:0) storeString.
- DecimalPointCharacterForPrinting := $.
+ DecimalPointCharacterForPrinting := $,.
+ 1.234 storeString.
+ 1.0 storeString.
+ 1e10 storeString.
+ 1.2e3 storeString.
+ 1.2e30 storeString.
+ (1.0 uncheckedDivide:0) storeString.
+ (0.0 uncheckedDivide:0) storeString.
+ DecimalPointCharacterForPrinting := $.
"
! !
@@ -1906,20 +2003,18 @@
^ self elementBoundsError:value
].
^ self indexNotIntegerOrOutOfBounds:index
-! !
-
-!Float methodsFor:'queries'!
+!
basicSize
"return the size in bytes of the float.
Notice:
- the need to redefine this method here is due to the
- inability of many machines to store floats in non-double aligned memory.
- Therefore, on some machines, the first 4 bytes of a float are left unused,
- and the actual float is stored at index 5 .. 12.
- To hide this at one place, this method knows about that, and returns
- values as if this filler wasnt present."
+ the need to redefine this method here is due to the
+ inability of many machines to store floats in non-double aligned memory.
+ Therefore, on some machines, the first 4 bytes of a float are left unused,
+ and the actual float is stored at index 5 .. 12.
+ To hide this at one place, this method knows about that, and returns
+ values as if this filler wasn't present."
%{ /* NOCONTEXT */
@@ -1927,6 +2022,39 @@
%}.
! !
+!Float methodsFor:'queries'!
+
+nextFloat:count
+ "answer the next float count places after (or before if count is negative) myself"
+
+%{
+#ifndef INT64
+# define INT64 long long int
+#endif
+ union {
+ double d;
+ INT64 i;
+ } this;
+
+ if (__isSmallInteger(count)) {
+ this.d = __floatVal(self);
+ if (isfinite(this.d))
+ this.i += __intVal(count);
+
+ RETURN(__MKFLOAT(this.d));
+ }
+%}.
+ self primitiveFailed:#badArgument
+
+ "
+ (1.0 nextFloat:1) storeString
+ (67329.234 nextFloat:1) storeString
+ (67329.234 asShortFloat nextFloat:1) storeString
+ Float NaN nextFloat:100000
+ Float infinity nextFloat:100000
+ "
+! !
+
!Float methodsFor:'special access'!
exponent
@@ -2888,11 +3016,11 @@
!Float class methodsFor:'documentation'!
version
- ^ '$Header: /cvs/stx/stx/libbasic/Float.st,v 1.198 2014-06-25 12:20:31 stefan Exp $'
+ ^ '$Header: /cvs/stx/stx/libbasic/Float.st,v 1.199 2014-06-30 14:17:28 stefan Exp $'
!
version_CVS
- ^ '$Header: /cvs/stx/stx/libbasic/Float.st,v 1.198 2014-06-25 12:20:31 stefan Exp $'
+ ^ '$Header: /cvs/stx/stx/libbasic/Float.st,v 1.199 2014-06-30 14:17:28 stefan Exp $'
! !