ArithmeticValue.st
changeset 21967 0a6e9e6e26f0
parent 21940 4720e68ae2b4
child 21991 0533f6b00cce
--- a/ArithmeticValue.st	Mon Jul 03 15:56:06 2017 +0200
+++ b/ArithmeticValue.st	Mon Jul 03 15:56:39 2017 +0200
@@ -47,29 +47,29 @@
     variables and signal accessors remain here for backward compatibility.
 
     [class variables:]
-	ArithmeticSignal        <Error>         parent of all arithmetic signals
-						(never raised itself)
-						New: now a reference to ArithmeticError
+        ArithmeticSignal        <Error>         parent of all arithmetic signals
+                                                (never raised itself)
+                                                New: now a reference to ArithmeticError
 
-	DomainErrorSignal       <Error>         raised upon float errors
-						(for example range in trigonometric)
-						New: now a reference to DomainError
+        DomainErrorSignal       <Error>         raised upon float errors
+                                                (for example range in trigonometric)
+                                                New: now a reference to DomainError; no longer a classVar
 
-	DivisionByZeroSignal    <Error>         raised when division by 0 is attempted
-						New: now a reference to ZeroDivide
+        DivisionByZeroSignal    <Error>         raised when division by 0 is attempted
+                                                New: now a reference to ZeroDivide
 
-	OverflowSignal          <Error>         raised on overflow/underflow conditions
-	UnderflowSignal                         in float arithmetic.
-						Notice: some OperatingSystems do not
-						provide enough information for ST/X to
-						extract the real reason for the floatException
-						thus raising DomainErrorSignal in these cases.
+        OverflowSignal          <Error>         raised on overflow/underflow conditions
+        UnderflowSignal                         in float arithmetic.
+                                                Notice: some OperatingSystems do not
+                                                provide enough information for ST/X to
+                                                extract the real reason for the floatException
+                                                thus raising DomainErrorSignal in these cases.
 
     [author:]
-	Claus Gittinger
+        Claus Gittinger
 
     [See also:]
-	Number
+        Number
 "
 ! !
 
@@ -105,6 +105,15 @@
     ^ ImaginaryResultError
 !
 
+infiniteResultSignal
+    "return the signal which is raised when an infinite result would be created
+     (such as when taking the logarithm of zero)"
+
+    ^ InfiniteResultError
+
+    "Created: / 03-07-2017 / 15:46:02 / cg"
+!
+
 operationNotPossibleSignal
     ^ Error
 !
@@ -150,12 +159,14 @@
     "/ messages (accessors) instead of referring to the class variables.
 
     ArithmeticSignal := ArithmeticError.
-    DomainErrorSignal := DomainError.
+    "/ DomainErrorSignal := DomainError.
     DivisionByZeroSignal := ZeroDivide.
     UnorderedSignal := UnorderedNumbersError.
     ConversionErrorSignal := ConversionError.
     OverflowSignal := OverflowError.
     UnderflowSignal := UnderflowError.
+
+    "Modified: / 03-07-2017 / 14:05:10 / cg"
 ! !
 
 !ArithmeticValue class methodsFor:'coercing & converting'!
@@ -250,6 +261,126 @@
     "Modified: / 16.11.2001 / 14:12:09 / cg"
 ! !
 
+!ArithmeticValue class methodsFor:'exception handling'!
+
+trapImaginary:aBlock
+    "evaluate aBlock; 
+     if any DomainError occurs inside, which would return an imaginary result, 
+     (eg. square root of negative number),
+     convert the result to a complex number and proceed.
+     
+     This allows for regular (failing) code to transparently convert to complex."
+
+    ^ ImaginaryResultError 
+        handle: [:ex |
+            |msgSend selector rcvr|
+
+            msgSend := ex parameter.
+            selector := msgSend selector.
+            rcvr := msgSend receiver.
+            (selector == #sqrt or: [selector == #sqrtTruncated]) ifTrue: [
+                (rcvr isNumber and:[rcvr isReal]) ifTrue:[
+                    ex proceedWith:(rcvr abs perform:selector) i
+                ] ifFalse:[    
+                    msgSend receiver: rcvr asComplex.
+                    ex proceedWith: msgSend value
+                ].
+            ] ifFalse: [
+                (selector == #integerSqrt) ifTrue: [
+                    (rcvr isInteger) ifTrue:[
+                        ex proceedWith:(rcvr abs integerSqrt) i
+                    ] ifFalse:[
+                        ex proceedWith:(rcvr abs asComplex sqrt floor) i
+                    ].    
+                ]
+            ].
+            ex reject
+        ] do: 
+            aBlock
+
+    "
+     this raises an error:
+         -2 sqrt
+
+     this returns an imaginary result:
+         Complex trapImaginary: [-2 sqrt]
+
+     of course, this one as well:
+         -2 asComplex sqrt
+    "
+
+    "failing code:
+         |a|
+
+         a := -2.
+         (a sqrt + 5) * 17.
+    "
+    "complex code:
+         |a|
+
+         Complex trapImaginary:[
+             a := -2.
+             (a sqrt + 5) * 2.
+         ]
+    "
+
+    "Modified: / 03-07-2017 / 15:53:49 / cg"
+!
+
+trapInfinity:aBlock
+    "evaluate aBlock; 
+     if any DomainError occurs inside, which would return an infinite result, 
+     (eg. ln of zero),
+     convert the result to infinity and proceed.
+     
+     This allows for regular (failing) code to transparently convert to infinity and behave
+     similar to other systems which do that."
+
+    ^ InfiniteResultError 
+        handle: [:ex |
+            |msgSend selector rcvr|
+
+            msgSend := ex parameter.
+            selector := msgSend selector.
+            rcvr := msgSend receiver.
+            ((selector == #ln) 
+            or:[selector == #log10
+            or:[selector == #integerLog10]]) ifTrue: [
+                (rcvr isNumber) ifTrue:[
+                    ex proceedWith:(rcvr class negativeInfinity )
+                ].
+            ].
+            ex reject
+        ] do: 
+            aBlock
+
+    "
+     this raises an error:
+         0 ln
+
+     this returns an imaginary result:
+         Number trapInfinity: [0 ln]
+    "
+
+    "failing code:
+         |a|
+
+         a := 0.
+         a ln isFinite.
+    "
+    "fixed code:
+         |a|
+
+         Number trapInfinity:[
+             a := 0.
+             a ln isFinite.
+         ]
+    "
+
+    "Created: / 03-07-2017 / 14:25:57 / cg"
+    "Modified: / 03-07-2017 / 15:53:40 / cg"
+! !
+
 !ArithmeticValue class methodsFor:'queries'!
 
 isAbstract
@@ -1226,7 +1357,8 @@
 !
 
 raisedFromNumber:aNumber
-    "aNumber does not know how to be raised to the receiver"
+    "aNumber does not know how to be raised to the receiver 
+     (i.e. how to compute aNumber^self)"
 
     ^ self class
         raise:#domainErrorSignal
@@ -1236,6 +1368,7 @@
         errorString:'bad receiver/arg in raisedTo:'
 
     "Created: / 01-07-2017 / 21:17:06 / cg"
+    "Modified (comment): / 03-07-2017 / 14:06:53 / cg"
 !
 
 remainderFromFloat:aFloat
@@ -1520,21 +1653,24 @@
 
 even
     "return true if the receiver is divisible by 2.
-     Only defined for whole-numbers."
+     This is only defined for whole-numbers (integers)."
 
     ^ self class
-        raise:DomainError
+        raise:#domainErrorSignal
         receiver:self
         selector:#even
         arguments:#()
         errorString:'Receiver must be a whole-number'
 
-    "/ No, this is bad: ^ self truncated asInteger even
+    "/ No no no, the following is bad: 
+    "/      ^ self truncated asInteger even
 
     "
-        2.4 even
-        2.0 even
+     2.4 even
+     2.0 even
     "
+
+    "Modified (comment): / 03-07-2017 / 14:10:01 / cg"
 !
 
 isComplex