[squeak-dev] The Inbox: Kernel-nice.690.mcz

Nicolas Cellier nicolas.cellier.aka.nice at gmail.com
Sun May 20 11:54:23 UTC 2012


I posted in inbox in order to obtain an agreement on features, rather
than begging for beta-testers.

For testing, Burger & Dybvig papers report that a set of 250 680 float
was used and compared to various libc printf (the linux and solaris
one were rounding correctly, the SGI, HP, RS6000 and Dec Alpha all
suffered from some incorrect rounding at time of writing).
We should really have such intensive tests, but maybe make them optional...

With our rational arithmetic, we can also test correct rounding
without a reference to external library :
1) we produce a printed representation
2) we re-interpret the printed representation in rational arithmetic
3) we test (original asTrueFraction - reconstructed) abs <= (original
ulp / 2) for free format
4) we test (original asTrueFraction - reconstructed) abs <= (((base
raisedTo: placesDesired) reciprocal max: original ulp) / 2) for fixed
format

But 2 bugs in printing and scanning could eventually compensate...
Testing that printed representation is the shortest form having this
property is a bit more involved...

Nicolas

2012/5/20 Bert Freudenberg <bert at freudenbergs.de>:
> Looks good to me - but I doubt you will get any testing unless you actually commit to trunk :)
>
> - Bert -
>
> On 20.05.2012, at 08:05, commits at source.squeak.org wrote:
>
>> A new version of Kernel was added to project The Inbox:
>> http://source.squeak.org/inbox/Kernel-nice.690.mcz
>>
>> ==================== Summary ====================
>>
>> Name: Kernel-nice.690
>> Author: nice
>> Time: 20 May 2012, 10:05:07.444 am
>> UUID: 2c2aba8b-d8bd-4ec3-8b88-a873de9c68c4
>> Ancestors: Kernel-nice.689
>>
>> 1) Correct the very new #printOn:maxDecimalPlaces: which often rounds inexactly for large Float or large number of digits.
>> Rationale: the printed representation shall be rounded exactly
>>
>> 2) Don't print arbitrary digits beyond Float precision, just print zeros
>> (0.1 printShowingDecimalPlaces: 20) now prints '0.10000000000000000000' instead of '0.10000000000000000555',
>> Rationale: those digits, while reflecting internal representation exactly, are totally insignificant and could be replaced with any other digits, while still representing the same Float.
>>
>> 3) Print the negative sign, even if the number vanishes to zero at prescribed decimal places
>> (-0.001 printShowingDecimalPlaces: 2) now prints '-0.00' instead of '0.00',
>> Rationale: this behaves like classical lib printf, and is in the spirit of Float negativeZero printString: when the precision vanishes, the Float keeps its sign.
>>
>> Note: I find the scheme feature which prints # for insignificant digits is very nice, but we should find a re-interpretable format...
>>
>> Implementation details:
>>
>> Of course, algorithms are taken from:
>> Robert G. Burger and R. Kent Dybvig
>>       Printing Floating Point Numbers Quickly and Accurately
>>       ACM SIGPLAN 1996 Conference on Programming Language Design and Implementation
>>       June 1996.
>>
>> Note that there is lot of duplicated code between free format and fixed format Float printing.
>> And we should also fix the case of fixed number of digits #absPrintOn:base:digitCount: ...
>>
>> =============== Diff against Kernel-nice.689 ===============
>>
>> Item was added:
>> + ----- Method: Float>>absPrintExactlyOn:base:decimalPlaces:showTrailingFractionalZeros: (in category 'printing') -----
>> + absPrintExactlyOn: aStream base: base decimalPlaces: placesDesired showTrailingFractionalZeros: showtrailingZeros
>> +     "Print my value on a stream in the given base with fixed number of digits after floating point.
>> +     When placesDesired are beyond Float precision, zeroes are appended.
>> +     When showtrailingZeros is false, the trailing zeroes after decimal point will be omitted.
>> +     If all fractional digits are zeros, the decimal point is omitted too.
>> +     Assumes that my value is strictly positive; negative numbers, zero, and NaNs have already been handled elsewhere.
>> +     Based upon the algorithm outlined in:
>> +     Robert G. Burger and R. Kent Dybvig
>> +     Printing Floating Point Numbers Quickly and Accurately
>> +     ACM SIGPLAN 1996 Conference on Programming Language Design and Implementation
>> +     June 1996.."
>> +
>> +     | significand exp baseExpEstimate r s mPlus mMinus scale roundingLowIncludesLimits roundingHighIncludesLimits d tc1 tc2 decPointCount slowbit shead delta |
>> +     self isInfinite ifTrue: [aStream nextPutAll: 'Infinity'. ^ self].
>> +     significand := self significandAsInteger.
>> +     exp := (self exponent - 52) max: MinValLogBase2.
>> +     exp >= 0
>> +             ifTrue:
>> +                     [significand ~= 16r10000000000000
>> +                             ifTrue:
>> +                                     [r := significand bitShift: 1 + exp.
>> +                                     s := 2.
>> +                                     mPlus := mMinus := 1 bitShift: exp]
>> +                             ifFalse:
>> +                                     [r := significand bitShift: 2 + exp.
>> +                                     s := 4.
>> +                                     mPlus := 2 * (mMinus := 1 bitShift: exp)]]
>> +             ifFalse:
>> +                     [(exp = MinValLogBase2 or: [significand ~= 16r10000000000000])
>> +                             ifTrue:
>> +                                     [r := significand bitShift: 1.
>> +                                     s := 1 bitShift: 1 - exp.
>> +                                     mPlus := mMinus := 1]
>> +                             ifFalse:
>> +                                     [r := significand bitShift: 2.
>> +                                     s := 1 bitShift: 2 - exp.
>> +                                     mPlus := 2.
>> +                                     mMinus := 1]].
>> +     delta := s / 2 / (base raisedTo: placesDesired).
>> +     roundingLowIncludesLimits :=  (mMinus < delta and: [mMinus := delta. true]) or: [significand even].
>> +     roundingHighIncludesLimits := (mPlus < delta and: [mPlus := delta. true]) or: [significand even].
>> +     baseExpEstimate := (self exponent * base asFloat reciprocalLogBase2 - 1.0e-10) ceiling.
>> +     baseExpEstimate >= 0
>> +             ifTrue: [s := s * (base raisedToInteger: baseExpEstimate)]
>> +             ifFalse:
>> +                     [scale := base raisedToInteger: baseExpEstimate negated.
>> +                     r := r * scale.
>> +                     mPlus := mPlus * scale.
>> +                     mMinus := mMinus * scale].
>> +     ((r + mPlus >= s) and: [roundingHighIncludesLimits or: [r + mPlus > s]])
>> +             ifTrue: [baseExpEstimate := baseExpEstimate + 1]
>> +             ifFalse:
>> +                     [r := r * base.
>> +                     mPlus := mPlus * base.
>> +                     mMinus := mMinus * base].
>> +     decPointCount := baseExpEstimate.
>> +     baseExpEstimate <= 0
>> +             ifTrue:
>> +                     [placesDesired + baseExpEstimate <= 0
>> +                             ifTrue:
>> +                                     [aStream nextPut: $0.
>> +                                     (showtrailingZeros and: [placesDesired > 0]) ifTrue: [aStream nextPut: $.; nextPutAll: (String new: placesDesired withAll: $0)].
>> +                                     ^self].
>> +                     aStream nextPutAll: '0.'; nextPutAll: (String new: 0 - baseExpEstimate withAll: $0)].
>> +     slowbit := 1 - s lowBit .
>> +     shead := s bitShift: slowbit.
>> +     [d := (r bitShift: slowbit) // shead.
>> +     r := r - (d * s).
>> +     (tc1 := (r <= mMinus) and: [roundingLowIncludesLimits or: [r < mMinus]]) |
>> +     (tc2 := (r + mPlus >= s) and: [roundingHighIncludesLimits or: [r + mPlus > s]])] whileFalse:
>> +             [aStream nextPut: (Character digitValue: d).
>> +             r := r * base.
>> +             mPlus := mPlus * base.
>> +             mMinus := mMinus * base.
>> +             (decPointCount := decPointCount - 1) = 0 ifTrue: [aStream nextPut: $.]].
>> +     tc2 ifTrue:
>> +             [(tc1 not or: [r * 2 >= s]) ifTrue: [d := d + 1]].
>> +     aStream nextPut: (Character digitValue: d).
>> +     decPointCount > 0
>> +             ifTrue:
>> +                     [decPointCount - 1 to: 1 by: -1 do: [:i | aStream nextPut: $0].
>> +                     (showtrailingZeros and: [placesDesired > 0]) ifTrue: [aStream nextPut: $.; nextPutAll: (String new: placesDesired withAll: $0)]]
>> +             ifFalse:
>> +                     [(showtrailingZeros and: [placesDesired + decPointCount > 1]) ifTrue: [aStream nextPutAll: (String new: placesDesired + decPointCount - 1 withAll: $0)]].!
>>
>> Item was changed:
>> + ----- Method: Float>>absPrintOn:base:digitCount: (in category 'printing') -----
>> - ----- Method: Float>>absPrintOn:base:digitCount: (in category 'private') -----
>>  absPrintOn: aStream base: base digitCount: digitCount
>>       "Print me in the given base, using digitCount significant figures."
>>
>>       | fuzz x exp q fBase scale logScale xi |
>>       self isInfinite ifTrue: [^ aStream nextPutAll: 'Inf'].
>>       fBase := base asFloat.
>>       "x is myself normalized to [1.0, fBase), exp is my exponent"
>>       exp :=
>>               self < 1.0
>>                       ifTrue: [self reciprocalFloorLog: fBase]
>>                       ifFalse: [self floorLog: fBase].
>>       scale := 1.0.
>>       logScale := 0.
>>       [(x := fBase raisedTo: (exp + logScale)) = 0]
>>               whileTrue:
>>                       [scale := scale * fBase.
>>                       logScale := logScale + 1].
>>       x := self * scale / x.
>>       fuzz := fBase raisedTo: 1 - digitCount.
>>       "round the last digit to be printed"
>>       x := 0.5 * fuzz + x.
>>       x >= fBase
>>               ifTrue:
>>                       ["check if rounding has unnormalized x"
>>                       x := x / fBase.
>>                       exp := exp + 1].
>>       (exp < 6 and: [exp > -4])
>>               ifTrue:
>>                       ["decimal notation"
>>                       q := 0.
>>                       exp < 0 ifTrue: [1 to: 1 - exp do: [:i | aStream nextPut: ('0.0000'
>>  at: i)]]]
>>               ifFalse:
>>                       ["scientific notation"
>>                       q := exp.
>>                       exp := 0].
>>       [x >= fuzz]
>>               whileTrue:
>>                       ["use fuzz to track significance"
>>                       xi := x asInteger.
>>                       aStream nextPut: (Character digitValue: xi).
>>                       x := x - xi asFloat * fBase.
>>                       fuzz := fuzz * fBase.
>>                       exp := exp - 1.
>>                       exp = -1 ifTrue: [aStream nextPut: $.]].
>>       [exp >= -1]
>>               whileTrue:
>>                       [aStream nextPut: $0.
>>                       exp := exp - 1.
>>                       exp = -1 ifTrue: [aStream nextPut: $.]].
>>       q ~= 0
>>               ifTrue:
>>                       [aStream nextPut: $e.
>>                       q printOn: aStream]!
>>
>> Item was added:
>> + ----- Method: Float>>printOn:maxDecimalPlaces: (in category 'printing') -----
>> + printOn: aStream maxDecimalPlaces: placesDesired
>> +     "Refine super implementation in order to avoid any rounding error caused by rounded or roundTo:"
>> +
>> +     self isFinite ifFalse: [^self printOn: aStream].
>> +     self > 0.0
>> +             ifTrue: [self absPrintExactlyOn: aStream base: 10 decimalPlaces: placesDesired showTrailingFractionalZeros: false]
>> +             ifFalse:
>> +                     [self sign = -1
>> +                             ifTrue: [aStream nextPutAll: '-'].
>> +                     self = 0.0
>> +                             ifTrue: [aStream nextPutAll: '0.0']
>> +                             ifFalse: [self absPrintExactlyOn: aStream base: 10 decimalPlaces: placesDesired showTrailingFractionalZeros: false]]!
>>
>> Item was added:
>> + ----- Method: Float>>printOn:showingDecimalPlaces: (in category 'printing') -----
>> + printOn: aStream showingDecimalPlaces: placesDesired
>> +     "Refine super implementation in order to avoid any rounding error caused by rounded or roundTo:"
>> +
>> +     self isFinite ifFalse: [^self printOn: aStream].
>> +     self > 0.0
>> +             ifTrue: [self absPrintExactlyOn: aStream base: 10 decimalPlaces: placesDesired showTrailingFractionalZeros: true]
>> +             ifFalse:
>> +                     [self sign = -1
>> +                             ifTrue: [aStream nextPutAll: '-'].
>> +                     self = 0.0
>> +                             ifTrue: [aStream nextPutAll: '0.0']
>> +                             ifFalse: [self absPrintExactlyOn: aStream base: 10 decimalPlaces: placesDesired showTrailingFractionalZeros: true]]!
>>
>> Item was removed:
>> - ----- Method: Float>>printShowingDecimalPlaces: (in category 'printing') -----
>> - printShowingDecimalPlaces: placesDesired
>> -     "This implementation avoids any rounding error caused by rounded or roundTo:"
>> -
>> -     self isFinite ifFalse: [^self printString].
>> -     ^self asTrueFraction printShowingDecimalPlaces: placesDesired!
>>
>>
>
>


More information about the Squeak-dev mailing list