--- a/SmallInteger.st Wed Oct 07 06:51:59 2015 +0200
+++ b/SmallInteger.st Fri Oct 09 06:49:31 2015 +0200
@@ -251,165 +251,193 @@
# endif
if (__isSmallInteger(aNumber)) {
- myValue = __intVal(self);
- otherValue = __intVal(aNumber);
+ myValue = __intVal(self);
+ otherValue = __intVal(aNumber);
# if defined(USE_LONGLONG_FOR_MUL)
- {
+ {
# if defined(__alpha__) && !defined(__alpha64__)
# define LONGLONG INT64
# else
# define LONGLONG long long
# endif
- LONGLONG product;
-
- product = (LONGLONG)myValue * (LONGLONG)otherValue;
- if ((product >= (LONGLONG)_MIN_INT)
- && (product <= (LONGLONG)_MAX_INT)) {
- RETURN ( __mkSmallInteger((INT)product) );
- }
- if (product < 0) {
- negative = -1;
- product = -product;
- } else {
- negative = 1;
- }
- productHi = product >> 32;
- productLow = product & 0xFFFFFFFFL;
- }
+ LONGLONG product;
+
+ product = (LONGLONG)myValue * (LONGLONG)otherValue;
+ if ((product >= (LONGLONG)_MIN_INT)
+ && (product <= (LONGLONG)_MAX_INT)) {
+ RETURN ( __mkSmallInteger((INT)product) );
+ }
+ if (product < 0) {
+ negative = -1;
+ product = -product;
+ } else {
+ negative = 1;
+ }
+ productHi = product >> 32;
+ productLow = product & 0xFFFFFFFFL;
+ }
# else /* no long-long */
- negative = 1;
- if (myValue < 0) {
- negative = -1;
- myValue = -myValue;
- }
- if (otherValue < 0) {
- negative = -negative;
- otherValue = -otherValue;
- }
+ negative = 1;
+ if (myValue < 0) {
+ negative = -1;
+ myValue = -myValue;
+ }
+ if (otherValue < 0) {
+ negative = -negative;
+ otherValue = -otherValue;
+ }
# if defined(__GNUC__) && defined(__mc68k__)
- asm ("mulu%.l %3,%1:%0"
- : "=d" ((unsigned long)(productLow)),
- "=d" ((unsigned long)(productHi))
- : "%0" ((unsigned long)(myValue)),
- "dmi" ((unsigned long)(otherValue)));
+ asm ("mulu%.l %3,%1:%0"
+ : "=d" ((unsigned long)(productLow)),
+ "=d" ((unsigned long)(productHi))
+ : "%0" ((unsigned long)(myValue)),
+ "dmi" ((unsigned long)(otherValue)));
# else
# if defined (__GNUC__) && defined(__i386__)
- asm ("mull %3"
- : "=a" ((unsigned long)(productLow)),
- "=d" ((unsigned long)(productHi))
- : "%0" ((unsigned long)(myValue)),
- "rm" ((unsigned long)(otherValue)));
+ asm ("mull %3"
+ : "=a" ((unsigned long)(productLow)),
+ "=d" ((unsigned long)(productHi))
+ : "%0" ((unsigned long)(myValue)),
+ "rm" ((unsigned long)(otherValue)));
# else
# if defined(WIN32) && defined(__BORLANDC__)
- asm {
- mov eax, myValue
- mov edx, otherValue
- mul edx
- mov productLow, eax
- mov productHi, edx
- }
+ asm {
+ mov eax, myValue
+ mov edx, otherValue
+ mul edx
+ mov productLow, eax
+ mov productHi, edx
+ }
# else /* generic */
- {
- unsigned INT pHH, pHL, pLH, pLL;
- unsigned INT low1, low2, hi1, hi2;
- unsigned INT t;
-
- /* unsigned multiply myValue * otherValue -> productHi, productLow
- *
- * this is too slow:
- * since most machines can do 32*32 to 64 bit multiply,
- * (or at least 32*32 with Overflow check)
- * - need more assembler (inline) functions here
- */
+ {
+ unsigned INT pHH, pHL, pLH, pLL;
+ unsigned INT low1, low2, hi1, hi2;
+ unsigned INT t;
+
+ /* unsigned multiply myValue * otherValue -> productHi, productLow
+ *
+ * this is too slow:
+ * since most machines can do 32*32 to 64 bit multiply,
+ * (or at least 32*32 with Overflow check)
+ * - need more assembler (inline) functions here
+ */
# if __POINTER_SIZE__ == 8
- low1 = low32Bits((unsigned INT)myValue);
- hi1 = hi32Bits((unsigned INT)myValue);
- low2 = low32Bits((unsigned INT)otherValue);
- hi2 = hi32Bits((unsigned INT)otherValue);
+ low1 = low32Bits((unsigned INT)myValue);
+ hi1 = hi32Bits((unsigned INT)myValue);
+ low2 = low32Bits((unsigned INT)otherValue);
+ hi2 = hi32Bits((unsigned INT)otherValue);
# define LLMASK 0xC000000000000000L
# else
- low1 = low16Bits((unsigned INT)myValue);
- hi1 = hi16Bits((unsigned INT)myValue);
- low2 = low16Bits((unsigned INT)otherValue);
- hi2 = hi16Bits((unsigned INT)otherValue);
+ low1 = low16Bits((unsigned INT)myValue);
+ hi1 = hi16Bits((unsigned INT)myValue);
+ low2 = low16Bits((unsigned INT)otherValue);
+ hi2 = hi16Bits((unsigned INT)otherValue);
# define LLMASK 0xC0000000
# endif
- pLH = low1 * hi2;
- pHL = hi1 * low2;
- pLL = low1 * low2;
- pHH = hi1 * hi2;
-
- /*
- * the common case ...
- */
- if ((pHL == 0)
- && (pLH == 0)
- && (pHH == 0)
- && ((pLL & LLMASK) == 0)) {
- if (negative < 0) {
- RETURN ( __mkSmallInteger(- ((INT)pLL)) );
- }
- RETURN ( __mkSmallInteger((INT)pLL) );
- }
-
- /*
- * pHH |--------|--------|
- * pLH |--------|--------|
- * pHL |--------|--------|
- * pLL |--------|--------|
- */
+ pLH = low1 * hi2;
+ pHL = hi1 * low2;
+ pLL = low1 * low2;
+ pHH = hi1 * hi2;
+
+ /*
+ * the common case ...
+ */
+ if ((pHL == 0)
+ && (pLH == 0)
+ && (pHH == 0)
+ && ((pLL & LLMASK) == 0)) {
+ if (negative < 0) {
+ RETURN ( __mkSmallInteger(- ((INT)pLL)) );
+ }
+ RETURN ( __mkSmallInteger((INT)pLL) );
+ }
+
+ /*
+ * pHH |--------|--------|
+ * pLH |--------|--------|
+ * pHL |--------|--------|
+ * pLL |--------|--------|
+ */
# if __POINTER_SIZE__ == 8
- t = low32Bits(pLH) + low32Bits(pHL) + hi32Bits(pLL);
- productLow = (t << 32) + low32Bits(pLL);
- productHi = pHH + hi32Bits(t) + hi32Bits(pHL) + hi32Bits(pLH);
+ t = low32Bits(pLH) + low32Bits(pHL) + hi32Bits(pLL);
+ productLow = (t << 32) + low32Bits(pLL);
+ productHi = pHH + hi32Bits(t) + hi32Bits(pHL) + hi32Bits(pLH);
# else
- t = low16Bits(pLH) + low16Bits(pHL) + hi16Bits(pLL);
- productLow = (t << 16) + low16Bits(pLL);
- productHi = pHH + hi16Bits(t) + hi16Bits(pHL) + hi16Bits(pLH);
+ t = low16Bits(pLH) + low16Bits(pHL) + hi16Bits(pLL);
+ productLow = (t << 16) + low16Bits(pLL);
+ productHi = pHH + hi16Bits(t) + hi16Bits(pHL) + hi16Bits(pLH);
# endif
- }
+ }
# endif /* ! WIN32 */
# endif /* ! (__GNUC__ && __i386__) */
# endif /* ! (__GNUC__ && __mc68k__) */
- if (productHi == 0) {
- if (negative < 0) {
- if (productLow <= -(_MIN_INT)) {
- RETURN ( __mkSmallInteger(-((INT)productLow)) );
- }
- } else {
- if (productLow <= _MAX_INT) {
- RETURN ( __mkSmallInteger(productLow) );
- }
- }
- }
+ if (productHi == 0) {
+ if (negative < 0) {
+ if (productLow <= -(_MIN_INT)) {
+ RETURN ( __mkSmallInteger(-((INT)productLow)) );
+ }
+ } else {
+ if (productLow <= _MAX_INT) {
+ RETURN ( __mkSmallInteger(productLow) );
+ }
+ }
+ }
# endif /* ! USE_LONGLONG */
# if __POINTER_SIZE__ == 8
- RETURN (__MKLARGEINT128(negative, productLow, productHi));
+ RETURN (__MKLARGEINT128(negative, productLow, productHi));
# else
- RETURN (__MKLARGEINT64(negative, productLow, productHi));
+ RETURN (__MKLARGEINT64(negative, productLow, productHi));
# endif
} else if (__isFloatLike(aNumber)) {
- OBJ newFloat;
- double val = (double)__intVal(self) * __floatVal(aNumber);
-
- __qMKFLOAT(newFloat, val);
- RETURN ( newFloat );
+ OBJ newFloat;
+ double val = (double)__intVal(self) * __floatVal(aNumber);
+
+ __qMKFLOAT(newFloat, val);
+ RETURN ( newFloat );
} else if (__isShortFloat(aNumber)) {
- OBJ newFloat;
- float val = (float)__intVal(self) * __shortFloatVal(aNumber);
-
- __qMKSFLOAT(newFloat, val);
- RETURN ( newFloat );
+ OBJ newFloat;
+ float val = (float)__intVal(self) * __shortFloatVal(aNumber);
+
+ __qMKSFLOAT(newFloat, val);
+ RETURN ( newFloat );
+ } else if (__isFractionLike(aNumber)) {
+ INT myValue = __intVal(self);
+ OBJ t = __FractionInstPtr(aNumber)->f_numerator;
+
+ if (myValue == 0) {
+ RETURN(__mkSmallInteger(0));
+ }
+
+ if (__isSmallInteger(t)) {
+ INT num = __intVal(t);
+ t = __FractionInstPtr(aNumber)->f_denominator;
+ if (__isSmallInteger(t)) {
+ INT prod = myValue * num;
+ if (prod / myValue == num) { // check for overflow
+ INT den = __intVal(t);
+ INT quo = prod / den;
+ if (quo * den == prod) { // check for integer result
+ RETURN ( __mkSmallInteger(quo) );
+ }
+ }
+ }
+ }
}
#endif /* not __SCHTEAM__ */
%}.
^ aNumber productFromInteger:self
+
+ "
+ 3 * (1/2)
+ 6 * (1/2)
+ 6 * (-1/2)
+ "
!
+ aNumber
@@ -593,12 +621,12 @@
The result is truncated toward negative infinity
and will be negative, if the operands signs differ.
The following is always true:
- (receiver // aNumber) * aNumber + (receiver \\ aNumber) = receiver
+ (receiver // aNumber) * aNumber + (receiver \\ aNumber) = receiver
Be careful with negative results: 9 // 4 -> 2, while -9 // 4 -> -3.
Especially surprising:
- -1 // 10 -> -1 (because -(1/10) is truncated towards next smaller integer, which is -1.
- -10 // 3 -> -4 (because -(10/3) is truncated towards next smaller integer, which is -4.
+ -1 // 10 -> -1 (because -(1/10) is truncated towards next smaller integer, which is -1.
+ -10 // 3 -> -4 (because -(10/3) is truncated towards next smaller integer, which is -4.
See #quo: which returns -2 in the above case and #rem: which is the corresponding remainder."
@@ -619,60 +647,97 @@
INT dividend, divisor, rslt;
if (__isSmallInteger(aNumber)) {
- divisor = __intVal(aNumber);
- if (divisor != 0) {
- dividend = __intVal(self);
- rslt = dividend / divisor;
- /*
- * Optimized to speed up positive result
- */
- if (rslt <= 0) {
- if (rslt == 0) {
- if ((dividend ^ divisor) < 0) {
- /*
- * result (negative) has been truncated toward 0.
- * Return -1, because we truncate toward negative inf.
- */
- rslt = -1;
- }
- } else {
- /*
- * If result (negative) has been truncated toward 0,
- * subtract 1, because we truncate toward negative inf.
- */
- if (divisor > 0) {
- if (rslt * divisor > dividend) {
- rslt--;
- }
- } else {
- if (rslt * divisor < dividend) {
- rslt--;
- }
- }
- }
- }
- RETURN ( __mkSmallInteger(rslt) );
- }
+ divisor = __intVal(aNumber);
+ if (divisor != 0) {
+ dividend = __intVal(self);
+ rslt = dividend / divisor;
+ /*
+ * Optimized to speed up positive result
+ */
+ if (rslt <= 0) {
+ if (rslt == 0) {
+ if ((dividend ^ divisor) < 0) {
+ /*
+ * result (negative) has been truncated toward 0.
+ * Return -1, because we truncate toward negative inf.
+ */
+ rslt = -1;
+ }
+ } else {
+ /*
+ * If result (negative) has been truncated toward 0,
+ * subtract 1, because we truncate toward negative inf.
+ */
+ if (divisor > 0) {
+ if (rslt * divisor > dividend) {
+ rslt--;
+ }
+ } else {
+ if (rslt * divisor < dividend) {
+ rslt--;
+ }
+ }
+ }
+ }
+ RETURN ( __mkSmallInteger(rslt) );
+ }
} else {
- if (__isFraction(aNumber)) {
- OBJ t;
- INT num, den;
-
- t = __FractionInstPtr(aNumber)->f_numerator;
- if (__isSmallInteger(t)) {
- num = __intVal(t);
- t = __FractionInstPtr(aNumber)->f_denominator;
- if (__isSmallInteger(t)) {
- den = __intVal(t);
- RETURN ( __mkSmallInteger(__intVal(self) * den / num ));
- }
- }
- }
+ if (__isFractionLike(aNumber)) {
+ OBJ t = __FractionInstPtr(aNumber)->f_numerator;
+ if (__isSmallInteger(t)) {
+ INT num = __intVal(t);
+ t = __FractionInstPtr(aNumber)->f_denominator;
+ if (__isSmallInteger(t)) {
+ INT den = __intVal(t);
+ INT dividend = __intVal(self);
+ INT prod;
+#if 0 && defined(__GNUC__) // supported from GCC 5
+ if (!__builtin_mul_overflow(myself, den, &prod)) {
+ goto out; // overflow, try harder...
+ }
+#else
+ prod = dividend * den;
+ // make sure, that no overflow occured
+ if (prod / den != dividend) {
+ goto out; // overflow, try harder...
+ }
+#endif
+ rslt = prod / num;
+
+ /*
+ * Optimized to speed up positive result
+ */
+ if (rslt <= 0) {
+ if (rslt == 0) {
+ if ((dividend ^ num) < 0) {
+ /*
+ * result (negative) has been truncated toward 0.
+ * Return -1, because we truncate toward negative inf.
+ */
+ rslt = -1;
+ }
+ } else {
+ /*
+ * If result (negative) has been truncated toward 0,
+ * subtract 1, because we truncate toward negative inf.
+ */
+ if (num > 0) {
+ if (rslt * num > prod) rslt--;
+ } else {
+ if (rslt * num < prod) rslt--;
+ }
+ }
+ }
+ RETURN ( __mkSmallInteger(rslt) );
+ }
+ }
+ }
}
+out:;
#endif /* not __SCHTEAM__ */
%}.
(aNumber = 0) ifTrue:[
- ^ ZeroDivide raiseRequestWith:thisContext.
+ ^ ZeroDivide raiseRequestWith:thisContext.
].
^ aNumber integerQuotientFromInteger:self
@@ -685,7 +750,17 @@
-1 // 2 ~~ -1 ifTrue:[self halt].
1 // -2 ~~ -1 ifTrue:[self halt].
-1 // -2 ~~ 0 ifTrue:[self halt].
+
+ -7 // (4/3)
+ -7 quo: (4/3)
+
+ 7 // (-4/3)
+ 7 quo: (-4/3)
+
10000 // 3600000 ~~ 0 ifTrue:[self halt].
+ 12 // (1 / 1000000000000000000)
+ 12 // (1 / 100000000000000)
+ 12 // 0.00000000000001s
9 quo:4 => 2
-9 quo:4 => -2
@@ -826,7 +901,7 @@
and the arguments value. The result is truncated towards zero
and negative, if the operands signs differ..
The following is always true:
- (receiver quo: aNumber) * aNumber + (receiver rem: aNumber) = receiver
+ (receiver quo: aNumber) * aNumber + (receiver rem: aNumber) = receiver
For positive results, this is the same as #//,
for negative results, the remainder is ignored.
I.e.: '9 // 4 = 2' and '-9 // 4 = -3'
@@ -836,33 +911,40 @@
#ifdef __SCHTEAM__
return context._RETURN( self.quotient(aNumber));
#else
- INT val;
-
if (__isSmallInteger(aNumber)) {
- val = __intVal(aNumber);
- if (val != 0) {
- RETURN ( __mkSmallInteger(__intVal(self) / val) );
- }
+ INT val = __intVal(aNumber);
+ if (val != 0) {
+ RETURN ( __mkSmallInteger(__intVal(self) / val) );
+ }
} else {
- if (__isFraction(aNumber)) {
- OBJ t;
- INT num, den;
-
- t = __FractionInstPtr(aNumber)->f_numerator;
- if (__isSmallInteger(t)) {
- num = __intVal(t);
- t = __FractionInstPtr(aNumber)->f_denominator;
- if (__isSmallInteger(t)) {
- den = __intVal(t);
- RETURN ( __mkSmallInteger(__intVal(self) * den / num ));
- }
- }
- }
+ if (__isFractionLike(aNumber)) {
+ OBJ t = __FractionInstPtr(aNumber)->f_numerator;
+ if (__isSmallInteger(t)) {
+ INT num = __intVal(t);
+ t = __FractionInstPtr(aNumber)->f_denominator;
+ if (__isSmallInteger(t)) {
+ INT den = __intVal(t);
+ INT myself = __intVal(self);
+ INT prod;
+#if 0 && defined(__GNUC__) // supported from GCC 5
+ if (__builtin_mul_overflow(myself, den, &prod)) {
+ RETURN ( __mkSmallInteger(prod / num ));
+ }
+#else
+ prod = myself * den;
+ // make sure, that no overflow occured
+ if (prod / den == myself) {
+ RETURN ( __mkSmallInteger(prod / num ));
+ }
+#endif
+ }
+ }
+ }
}
#endif /* not __SCHTEAM__ */
%}.
(aNumber = 0) ifTrue:[
- ^ ZeroDivide raiseRequestWith:thisContext.
+ ^ ZeroDivide raiseRequestWith:thisContext.
].
^ self retry:#quo: coercing:aNumber
@@ -871,10 +953,17 @@
-9 // 4
9 quo:4
-9 quo:4
+
+ -7 // (4/3)
+ -7 quo: (4/3)
+
+ 7 // (-4/3)
+ 7 quo: (-4/3)
"
! !
+
!SmallInteger methodsFor:'bit operators'!
bitAnd:anInteger