Issue 4948 in pharo: Request for a clearer and faster #asFloat conversion

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Issue 4948 in pharo: Request for a clearer and faster #asFloat conversion

pharo
Status: Accepted
Owner: ----
Labels: Milestone-1.4 Type-Feature

New issue 4948 by [hidden email]: Request for a clearer and faster  
#asFloat conversion
http://code.google.com/p/pharo/issues/detail?id=4948

Nicolas Cellier uploaded a new version of Kernel to project The Trunk:
http://source.squeak.org/trunk/Kernel-nice.627.mcz

==================== Summary ====================

Name: Kernel-nice.627
Author: nice
Time: 25 September 2011, 12:55:51.623 pm
UUID: c09ead19-3aa6-482b-b2cc-f6549bc67eb6
Ancestors: Kernel-ul.626

Improve Fraction and Integer asFloat by:
- removing unreachable branch
- removing un-needed arithmetic
- renaming temps
- using Float precision explicitly rather than hardcoded magical numbers
- clarifying comments
The resulting code shall be clearer and a bit faster than previously.

=============== Diff against Kernel-ul.626 ===============

Item was changed:
  ----- Method: Fraction>>asFloat (in category 'converting') -----
  asFloat
        "Answer a Float that closely approximates the value of the receiver.
+       This implementation will answer the closest floating point number  
to the receiver.
+       In case of a tie, it will use the IEEE 754 round to nearest even  
mode.
+       In case of overflow, it will answer +/- Float infinity."
+
+       | a b mantissa exponent hasTruncatedBits lostBit n ha hb hm |
-       This implementation will answer the closest floating point number to
-       the receiver.
-       It uses the IEEE 754 round to nearest even mode
-       (can happen in case denominator is a power of two)"
-
-       | a b q r exponent floatExponent n ha hb hq q1 |
        a := numerator abs.
+       b := denominator.       "denominator is always positive"
-       b := denominator abs.
        ha := a highBit.
        hb := b highBit.

+       "Number of bits to keep in mantissa plus one to handle rounding."
+       n := 1 + Float precision.
+
        "If both numerator and denominator are represented exactly in  
floating point number,
+       then fastest thing to do is to use hardwired float division."
+       (ha < n and: [hb < n]) ifTrue: [^numerator asFloat / denominator  
asFloat].
+
+       "Shift the fraction by a power of two exponent so as to obtain a  
mantissa with n bits.
+       First guess is rough, the mantissa might have n+1 bits."
+       exponent := ha - hb - n.
+       exponent >= 0
-       then fastest thing to do is to use hardwired float division"
-       (ha < 54 and: [hb < 54]) ifTrue: [^numerator asFloat / denominator  
asFloat].
-
-       "Try and obtain a mantissa with 54 bits.
-       First guess is rough, we might get one more bit or one less"
-       exponent := ha - hb - 54.
-       exponent > 0
                ifTrue: [b := b bitShift: exponent]
                ifFalse: [a := a bitShift: exponent negated].
+       mantissa := a quo: b.
+       hasTruncatedBits := a > (mantissa * b).
+       hm := mantissa highBit.
-       q := a quo: b.
-       r := a - (q * b).
-       hq := q highBit.

+       "Check for gradual underflow, in which case the mantissa will loose  
bits.
+       Keep at least one bit to let underflow preserve the sign of zero."
+       lostBit := Float emin - (exponent + hm - 1).
+       lostBit > 0 ifTrue: [n := n - lostBit max: 1].
+
+       "Remove excess bits in the mantissa."
+       hm > n
+               ifTrue:
+                       [exponent := exponent + hm - n.
+                       hasTruncatedBits := hasTruncatedBits or: [mantissa  
anyBitOfMagnitudeFrom: 1 to: hm - n].
+                       mantissa := mantissa bitShift: n - hm].
+
+       "Check if mantissa must be rounded upward.
+       The case of tie (mantissa odd & hasTruncatedBits not)
+       will be handled by Integer>>asFloat."
+       (hasTruncatedBits and: [mantissa odd])
+               ifTrue: [mantissa := mantissa + 1].
+
-       "check for gradual underflow, in which case we should use less bits"
-       floatExponent := exponent + hq - 1.
-       n := floatExponent > -1023
-               ifTrue: [54]
-               ifFalse: [54 + floatExponent + 1022].
-
-       hq > n
-               ifTrue: [exponent := exponent + hq - n.
-                       r := (q bitAnd: (1 bitShift: hq - n) - 1) * b + r.
-                       q := q bitShift: n - hq].
-       hq < n
-               ifTrue: [exponent := exponent + hq - n.
-                       q1 := (r bitShift: n - hq) quo: b.
-                       q := (q bitShift: n - hq) bitAnd: q1.
-                       r := (r bitShift: n - hq) - (q1 * b)].
-
-       "check if we should round upward.
-       The case of exact half (q bitAnd: 1) isZero not & (r isZero)
-       will be handled by Integer>>asFloat"
-       ((q bitAnd: 1) isZero or: [r isZero])
-               ifFalse: [q := q + 1].
-
        ^ (self positive
+                       ifTrue: [mantissa asFloat]
+                       ifFalse: [mantissa asFloat negated])
-               ifTrue: [q asFloat]
-               ifFalse: [q = 0
-                       ifTrue: [Float negativeZero]
-                       ifFalse: [q asFloat negated]])
                timesTwoPower: exponent!

Item was changed:
  ----- Method: Integer>>asFloat (in category 'converting') -----
  asFloat
+       "Answer a Float that best approximates the value of the receiver."
-       "Answer a Float that represents the value of the receiver.
-       Optimized to process only the significant digits of a LargeInteger.
-       SqR: 11/30/1998 21:1

+       self subclassResponsibility!
-       This algorithm does honour IEEE 754 round to nearest even mode.
-       Numbers are first rounded on nearest integer on 53 bits.
-       In case of exact half difference between two consecutive integers  
(2r0.1),
-       there are two possible choices (two integers are as near, 0 and 1)
-       In this case, the nearest even integer is chosen.
-       examples (with less than 53bits for clarity)
-       2r0.00001 is rounded to 2r0
-       2r1.00001 is rounded to 2.1
-       2r0.1 is rounded to 2r0 (nearest event)
-       2r1.1 is rounded to 2.10 (neraest even)
-       2r0.10001 is rounded to 2r1
-       2r1.10001 is rounded to 2.10"
-
-       | abs shift sum delta mask trailingBits carry |
-       self isZero
-               ifTrue: [^ 0.0].
-       abs := self abs.
-
-       "Assume Float is a double precision IEEE 754 number with 53bits  
mantissa.
-       We should better use some Float class message for that (Float  
precision)..."
-       delta := abs highBit - 53.
-       delta > 0
-               ifTrue: [mask := (1 bitShift: delta) - 1.
-                       trailingBits := abs bitAnd: mask.
-                       "inexact := trailingBits isZero not."
-                       carry := trailingBits bitShift: 1 - delta.
-                       abs := abs bitShift: delta negated.
-                       shift := delta.
-                       (carry isZero
-                                       or: [(trailingBits bitAnd: (mask  
bitShift: -1)) isZero
-                                                       and: [abs even]])
-                               ifFalse: [abs := abs + 1]]
-               ifFalse: [shift := 0].
-
-       "now, abs has no more than 53 bits, we can do exact floating point  
arithmetic"
-       sum := 0.0.
-       1 to: abs size do:
-               [:byteIndex |
-               sum := ((abs digitAt: byteIndex) asFloat timesTwoPower:  
shift) + sum.
-               shift := shift + 8].
-       ^ self positive
-                       ifTrue: [sum]
-                       ifFalse: [sum negated]!

Item was added:
+ ----- Method: LargeNegativeInteger>>asFloat (in category 'converting')  
-----
+ asFloat
+       ^self negated asFloat negated!

Item was added:
+ ----- Method: LargePositiveInteger>>asFloat (in category 'converting')  
-----
+ asFloat
+       "Answer a Float that best approximates the value of the receiver.
+       This algorithm is optimized to process only the significant digits  
of a LargeInteger.
+       And it does honour IEEE 754 round to nearest even mode in case of  
excess precision (see details below)."
+
+       "How numbers are rounded in IEEE 754 default rounding mode:
+       A shift is applied so that the highest 53 bits are placed before  
the floating point to form a mantissa.
+       The trailing bits form the fraction part placed after the floating  
point.
+       This fractional number must be rounded to the nearest integer.
+       If fraction part is 2r0.1, exactly between two consecutive  
integers, there is a tie.
+       The nearest even integer is chosen in this case.
+       Examples (First 52bits of mantissa are omitted for brevity):
+       2r0.00001 is rounded downward to 2r0
+       2r1.00001 is rounded downward to 2r1
+       2r0.1 is a tie and rounded to 2r0 (nearest even)
+       2r1.1 is a tie and rounded to 2r10 (nearest even)
+       2r0.10001 is rounded upward to 2r1
+       2r1.10001 is rounded upward to 2r10
+       Thus, if the next bit after floating point is 0, the mantissa is  
left unchanged.
+       If next bit after floating point is 1, an odd mantissa is always  
rounded upper.
+       An even mantissa is rounded upper only if the fraction part is not  
a tie."
+
+       "Algorihm details:
+       Floating point hardware will correctly handle the rounding by  
itself with a single inexact operation if mantissa has one excess bit of  
precision.
+       Except in the last case when extra bits are present after an even  
mantissa, we must round upper by ourselves.
+       Note 1: the inexact flag in floating point hardware must not be  
trusted because it won't take into account the bits we truncated by  
ourselves.
+       Note 2: the floating point hardware is presumed configured in  
default rounding mode."
+
+       | mantissa shift sum excess |
+
+       "Check how many bits excess the maximum precision of a Float  
mantissa."
+       excess := self highBitOfMagnitude - Float precision.
+       excess > 1
+               ifTrue:
+                       ["Remove the excess bits but one."
+                       mantissa := self bitShift: 1 - excess.
+                       shift := excess - 1.
+                       "Handle the case of extra bits truncated after an  
even mantissa."
+                       ((mantissa bitAnd: 2r11) = 2r01 and: [self  
anyBitOfMagnitudeFrom: 1 to: shift])
+                               ifTrue: [mantissa := mantissa + 1]]
+               ifFalse:
+                       [mantissa := self.
+                       shift := 0].
+
+       "Now that mantissa has at most 1 excess bit of precision, let  
floating point operations perform the final rounding."
+       sum := 0.0.
+       1 to: mantissa digitLength do:
+               [:byteIndex |
+               sum := sum + ((mantissa digitAt: byteIndex) asFloat  
timesTwoPower: shift).
+               shift := shift + 8].
+       ^sum!


_______________________________________________
Pharo-bugtracker mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-bugtracker
Reply | Threaded
Open this post in threaded view
|

Re: Issue 4948 in pharo: Request for a clearer and faster #asFloat conversion

pharo

Comment #1 on issue 4948 by [hidden email]: Request for a clearer  
and faster #asFloat conversion
http://code.google.com/p/pharo/issues/detail?id=4948

Frank Shearar said:
The exec summary of most of the changeset is making Integer >> asFloat
be subclassResponsibility. The functionality moves to
LargePositiveInteger >> asFloat. LargeNegativeInteger delegates to
LargePositiveInteger, and SmallInteger uses primitive 40.


_______________________________________________
Pharo-bugtracker mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-bugtracker
Reply | Threaded
Open this post in threaded view
|

Re: Issue 4948 in pharo: Request for a clearer and faster #asFloat conversion

pharo

Comment #2 on issue 4948 by [hidden email]: Request for a clearer  
and faster #asFloat conversion
http://code.google.com/p/pharo/issues/detail?id=4948

A new version of Kernel was added to project The Inbox:
http://source.squeak.org/inbox/Kernel-nice.630.mcz

==================== Summary ====================

Name: Kernel-nice.630
Author: nice
Time: 26 September 2011, 10:20:03.656 pm
UUID: 741a02d5-c9f3-4549-8013-a77ffe1305b1
Ancestors: Kernel-nice.629

With the help of Andres remarks, and a bit of magic (7 and 30), here is an  
ever faster LargeInteger>>asFloat. Non portable to other precision than  
double, but cute.

=============== Diff against Kernel-nice.629 ===============

Item was changed:
  ----- Method: LargePositiveInteger>>asFloat (in category 'converting')  
-----
  asFloat
        "Answer a Float that best approximates the value of the receiver.
        This algorithm is optimized to process only the significant digits  
of a LargeInteger.
        And it does honour IEEE 754 round to nearest even mode in case of  
excess precision (see details below)."

        "How numbers are rounded in IEEE 754 default rounding mode:
        A shift is applied so that the highest 53 bits are placed before the  
floating point to form a mantissa.
        The trailing bits form the fraction part placed after the floating  
point.
        This fractional number must be rounded to the nearest integer.
        If fraction part is 2r0.1, exactly between two consecutive integers,  
there is a tie.
        The nearest even integer is chosen in this case.
        Examples (First 52bits of mantissa are omitted for brevity):
        2r0.00001 is rounded downward to 2r0
        2r1.00001 is rounded downward to 2r1
        2r0.1 is a tie and rounded to 2r0 (nearest even)
        2r1.1 is a tie and rounded to 2r10 (nearest even)
        2r0.10001 is rounded upward to 2r1
        2r1.10001 is rounded upward to 2r10
        Thus, if the next bit after floating point is 0, the mantissa is  
left unchanged.
        If next bit after floating point is 1, an odd mantissa is always  
rounded upper.
        An even mantissa is rounded upper only if the fraction part is not a  
tie."

        "Algorihm details:
+       Floating point hardware will correctly handle the rounding by  
itself if there is a single inexact operation.
-       Floating point hardware will correctly handle the rounding by  
itself with a single inexact operation if mantissa has one excess bit of  
precision.
        Except in the last case when extra bits are present after an even  
mantissa, we must round upper by ourselves.
        Note 1: the inexact flag in floating point hardware must not be  
trusted because it won't take into account the bits we truncated by  
ourselves.
        Note 2: the floating point hardware is presumed configured in  
default rounding mode."

+       | mantissa shift excess |
-       | mantissa shift sum excess |

        "Check how many bits excess the maximum precision of a Float  
mantissa."
        excess := self highBitOfMagnitude - Float precision.
+       excess > 7
-       excess > 1
                ifTrue:
+                       ["Remove the excess bits but seven.
+                       Float precision + 7 = 60 bits = 2 * length of  
positive small integer"
+                       mantissa := self bitShiftMagnitude: 7 - excess.
+                       shift := excess - 7.
-                       ["Remove the excess bits but one."
-                       mantissa := self bitShift: 1 - excess.
-                       shift := excess - 1.
                        "Handle the case of extra bits truncated after an  
even mantissa."
+                       ((mantissa digitAt: 1) = 2r01000000 and: [self  
anyBitOfMagnitudeFrom: 1 to: shift])
-                       ((mantissa bitAnd: 2r11) = 2r01 and: [self  
anyBitOfMagnitudeFrom: 1 to: shift])
                                ifTrue: [mantissa := mantissa + 1]]
                ifFalse:
                        [mantissa := self.
                        shift := 0].

+       "Combine two small integers (SmallInteger maxVale highBit = 30  
bits) asFloat with a single inexact round off"
+       ^(((mantissa bitShiftMagnitude: -30) asFloat timesTwoPower: 30)
+               + (mantissa bitAnd: 16r3FFFFFFF "SmallInteger maxVal"))
+                       timesTwoPower: shift!
-       "Now that mantissa has at most 1 excess bit of precision, let  
floating point operations perform the final rounding."
-       sum := 0.0.
-       1 to: mantissa digitLength do:
-               [:byteIndex |
-               sum := sum + ((mantissa digitAt: byteIndex) asFloat  
timesTwoPower: shift).
-               shift := shift + 8].
-       ^sum!



_______________________________________________
Pharo-bugtracker mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-bugtracker
Reply | Threaded
Open this post in threaded view
|

Re: Issue 4948 in pharo: Request for a clearer and faster #asFloat conversion

pharo

Comment #3 on issue 4948 by [hidden email]: Request for a clearer  
and faster #asFloat conversion
http://code.google.com/p/pharo/issues/detail?id=4948

Note: these changed were reviewed here:
http://smallissimo.blogspot.com/2011/09/clarifying-and-optimizing.html
http://smallissimo.blogspot.com/2011/09/reviewing-fraction-asfloat.html


_______________________________________________
Pharo-bugtracker mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-bugtracker
Reply | Threaded
Open this post in threaded view
|

Re: Issue 4948 in pharo: Request for a clearer and faster #asFloat conversion

pharo
Updates:
        Status: FixToInclude

Comment #4 on issue 4948 by [hidden email]: Request for a clearer  
and faster #asFloat conversion
http://code.google.com/p/pharo/issues/detail?id=4948

I put a SLICE in the inbox with Squeak version (not the fastest possible).
A faster conversion as reported above (  
http://source.squeak.org/inbox/Kernel-nice.630.mcz ) rely on this:

1) use 7 excess bits for the mantissa (Float precision + 7 = 60 bits)
2) split the asFloat conversion into to SmallIntegers (30 bits each)
    These two conversions are exact and only a single inexact operation  
occurs
3) test the case of tie with a digitAt: operation, which is cheap
4) the 7 excess bits reduce the need for looking additional trailing bits  
in 1 case out of 256


_______________________________________________
Pharo-bugtracker mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-bugtracker
Reply | Threaded
Open this post in threaded view
|

Re: Issue 4948 in pharo: Request for a clearer and faster #asFloat conversion

pharo
Updates:
        Status: Integrated

Comment #5 on issue 4948 by [hidden email]: Request for a clearer  
and faster #asFloat conversion
http://code.google.com/p/pharo/issues/detail?id=4948

Integrated in 14212




_______________________________________________
Pharo-bugtracker mailing list
[hidden email]
http://lists.gforge.inria.fr/cgi-bin/mailman/listinfo/pharo-bugtracker