#FEATURE by cg
authorClaus Gittinger <cg@exept.de>
Tue, 08 May 2018 10:40:28 +0200
changeset 22786 7375ac3023c4
parent 22785 1d67d6adbbe5
child 22787 b4f6d9a8bf5e
#FEATURE by cg class: LimitedPrecisionReal added: #truncatedToPrecision
LimitedPrecisionReal.st
--- a/LimitedPrecisionReal.st	Tue May 08 09:05:13 2018 +0200
+++ b/LimitedPrecisionReal.st	Tue May 08 10:40:28 2018 +0200
@@ -1249,7 +1249,6 @@
 ! !
 
 
-
 !LimitedPrecisionReal methodsFor:'queries'!
 
 decimalPrecision
@@ -1426,7 +1425,6 @@
    ^ 0
 ! !
 
-
 !LimitedPrecisionReal methodsFor:'testing'!
 
 isFinite
@@ -1557,6 +1555,97 @@
     "
      0.4 asLongFloat truncatedAsFloat
     "
+!
+
+truncatedToPrecision
+    "truncates to the precision of the float.
+     This is slightly different from truncated.
+     Taking for example 1e32,
+     the printed representation will be 1e32,
+     but the actual value, when truncating to an integer
+     would be 100000003318135351409612647563264.
+
+     This is due to the inaccuracy in the least significant bits,
+     and the way the print-converter compensates for this.
+     This method tries to generate an integer value which corresponds
+     to what is seen in the float's printString.
+
+     Here, a slow fallback (generating and rescanning the printString)
+     is provided, which should work on any float number.
+     Specialized versions in subclasses may be added for more performance
+     (however, this is probably only used rarely)"
+
+    |pow10 printString intVal s sign nFract ch expSign exp pI|
+
+    pow10 := #(10 100 1000 10000 100000 1000000 10000000 100000000 1000000000).
+
+    printString := self printString.
+    intVal := 0.
+    sign := 1.
+    nFract := 0.
+    exp := 0.
+
+    "/ fetch the mantissa
+    s := printString readStream.
+    s peek == $- ifTrue:[
+        sign := -1.
+        s next.
+    ].
+    intVal := s nextDecimalInteger.
+    s peek == $. ifTrue:[
+        s next.
+        [(ch := s peek) notNil and:[ch isDigit]] whileTrue:[
+            intVal := intVal * 10 + (s next digitValue).
+            nFract := nFract + 1.
+        ].
+    ].
+    ('eEdDqQ' includes:s peek) ifTrue:[
+        expSign := 1.
+        s next.
+        (ch := s peek) == $- ifTrue:[
+            expSign := -1.
+            s next.
+        ] ifFalse:[
+            ch == $+ ifTrue:[
+                s next.
+            ].
+        ].
+        exp := s nextDecimalInteger.
+        exp := exp * expSign.
+    ].
+    exp := exp - nFract.
+    exp < 0 ifTrue:[
+        [exp < 0] whileTrue:[
+            pI := (exp negated) min:pow10 size.
+            intVal := intVal / (pow10 at:pI).
+            exp := exp + pI.
+        ].
+        intVal := intVal asInteger.
+    ] ifFalse:[
+        [exp > 0] whileTrue:[
+            pI := exp min:pow10 size.
+            intVal := intVal * (pow10 at:pI).
+            exp := exp - pI.
+        ].
+    ].
+    ^ intVal
+
+    "
+     1e32 asShortFloat truncated
+     1e32 asShortFloat truncatedToPrecision
+     1.234e10 asShortFloat truncatedToPrecision
+     1234e-1 asShortFloat truncatedToPrecision
+
+     1e32 truncated
+     1e32 truncatedToPrecision
+     1.234e10 truncatedToPrecision
+     1234e-1 truncatedToPrecision
+
+     1e32 asLongFloat truncated
+     1e32 asLongFloat truncatedToPrecision
+     1.234e10 asLongFloat truncatedToPrecision
+     1234e-1 asLongFloat truncatedToPrecision
+    "
 ! !
 
 !LimitedPrecisionReal methodsFor:'visiting'!