'From Squeak3.10.1-basic of 13 May 2008 [latest update: #7164] on 20 May 2008 at 7:10:53 am'!
"Change Set: SqNumberParser_speedUp_M6976_nice
Date: 10 March 2008
Author: nice
This changeSet makes SqNumberParser about twice and up to four times faster than it used to in Float Number crunching.
This is http://bugs.squeak.org/view.php?id=6976.
The four improvements are:
1) Inline digitValue when radix <= 10. This is the main gain.
2) Don't test 'NaN' and 'Infinity' in preamble.
Test only in exceptional conditions (when no digit were found).
3) hack (aRadix raisedTo: exponent) with base 2 bitShift: arithmetic
and use timesTwoPower: a posteriori to avoid some LargePositiveInteger.
4) hack PositionableStream>>position:
replace & condition by and: which is inlined by Compiler"!
!PositionableStream methodsFor: 'positioning' stamp: 'nice 3/10/2008 22:29'!
position: anInteger
"Set the current position for accessing the objects to be anInteger, as long
as anInteger is within the bounds of the receiver's contents. If it is not,
create an error notification."
(anInteger >= 0 and: [anInteger <= readLimit])
ifTrue: [position := anInteger]
ifFalse: [self positionError]! !
!SqNumberParser methodsFor: 'parsing-private' stamp: 'nice 3/10/2008 23:13'!
makeFloatFromMantissa: m exponent: k base: aRadix
"Convert infinite precision arithmetic into Floating point.
This alogrithm rely on correct IEEE rounding mode
being implemented in Integer>>asFloat and Fraction>>asFloat"
| p |
p := aRadix lowBit - 1.
^k positive
ifTrue: [(m * (((aRadix bitShift: p negated) raisedToInteger: k))) asFloat timesTwoPower: p*k]
ifFalse: [(Fraction numerator: m denominator: (((aRadix bitShift: p negated) raisedToInteger: k negated) bitShift: p*k negated)) asFloat]! !
!SqNumberParser methodsFor: 'parsing-public' stamp: 'nice 3/10/2008 22:50'!
nextNumber
"main method for reading a number.
This one can read Float Integer and ScaledDecimal"
| numberOfTrailingZeroInIntegerPart numberOfNonZeroFractionDigits mantissa decimalMultiplier decimalFraction value numberOfTrailingZeroInFractionPart |
neg := sourceStream peekFor: $-.
integerPart := self nextUnsignedIntegerBase: base ifFail: [
"This is not a regular number beginning with a digit
It is time to check for exceptional condition NaN and Infinity"
neg ifFalse: [(sourceStream nextMatchAll: 'NaN')
ifTrue: [^ Float nan]].
(sourceStream nextMatchAll: 'Infinity')
ifTrue: [^ neg
ifTrue: [Float infinity negated]
ifFalse: [Float infinity]].
^self expected: ['a digit between 0 and 9']].
numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero.
(sourceStream peekFor: $r)
ifTrue: ["r"
(base := integerPart) < 2
ifTrue: [^ self expected: 'an integer greater than 1 as valid radix'].
(sourceStream peekFor: $-)
ifTrue: [neg := neg not].
integerPart := self nextUnsignedIntegerBase: base.
numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero].
^ (sourceStream peekFor: $.)
ifTrue: [fractionPart := self
nextUnsignedIntegerBase: base
ifFail: [sourceStream skip: -1.
^ neg
ifTrue: [integerPart negated]
ifFalse: [integerPart]].
numberOfNonZeroFractionDigits := lastNonZero.
numberOfTrailingZeroInFractionPart := nDigits - lastNonZero.
self readExponent
ifFalse: [self readScale
ifTrue: [decimalMultiplier := base raisedTo: numberOfNonZeroFractionDigits.
decimalFraction := integerPart * decimalMultiplier + fractionPart / decimalMultiplier.
neg
ifTrue: [decimalFraction := decimalFraction negated].
^ ScaledDecimal newFromNumber: decimalFraction scale: scale]].
fractionPart isZero
ifTrue: [mantissa := integerPart
// (base raisedTo: numberOfTrailingZeroInIntegerPart).
exponent := exponent + numberOfTrailingZeroInIntegerPart]
ifFalse: [mantissa := integerPart
* (base raisedTo: numberOfNonZeroFractionDigits) + (fractionPart // (base raisedTo: numberOfTrailingZeroInFractionPart)).
exponent := exponent - numberOfNonZeroFractionDigits].
"very naive algorithm"
value := self makeFloatFromMantissa: mantissa exponent: exponent base: base.
^ neg
ifTrue: [value isZero
ifTrue: [Float negativeZero]
ifFalse: [value negated]]
ifFalse: [value]]
ifFalse: [self makeIntegerOrScaledInteger]! !
!SqNumberParser methodsFor: 'parsing-public' stamp: 'nice 3/10/2008 22:44'!
nextUnsignedIntegerBase: aRadix
"Form an unsigned integer with incoming digits from sourceStream.
Count the number of digits and the lastNonZero digit and store int in
instVar "
^ self
nextUnsignedIntegerBase: aRadix
ifFail: [self expected: ('a digit between 0 and ' copyWith: (Character digitValue: aRadix - 1))]! !
!SqNumberParser methodsFor: 'parsing-public' stamp: 'nice 3/10/2008 22:27'!
nextUnsignedIntegerBase: aRadix ifFail: errorBlock
"Form an unsigned integer with incoming digits from sourceStream.
Count the number of digits and the lastNonZero digit and store int in instVar"
| value digit |
value := 0.
nDigits := 0.
lastNonZero := 0.
aRadix <= 10
ifTrue: ["Avoid using digitValue which is awfully slow"
[sourceStream atEnd
or: [digit := sourceStream next charCode - 48.
(digit < 0
or: [digit >= aRadix])
and: [sourceStream skip: -1.
true]]]
whileFalse: [nDigits := nDigits + 1.
digit isZero
ifFalse: [lastNonZero := nDigits].
value := value * aRadix + digit]]
ifFalse: [
[sourceStream atEnd
or: [digit := sourceStream next digitValue.
(digit < 0
or: [digit >= aRadix])
and: [sourceStream skip: -1.
true]]]
whileFalse: [nDigits := nDigits + 1.
digit isZero
ifFalse: [lastNonZero := nDigits].
value := value * aRadix + digit]].
nDigits = 0
ifTrue: [errorBlock value].
^value! !