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

commits at source.squeak.org commits at source.squeak.org
Thu Nov 3 20:17:45 UTC 2011

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

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

Name: Kernel-nice.650
Author: nice
Time: 3 November 2011, 9:17:16.187 pm
UUID: e8775ee0-9502-469d-81b5-e7c39f9947a0
Ancestors: Kernel-eem.648

Provide a Float>>asMinimalDecimalFraction
which answers the shortest decimal Fraction that will equal the receiver when converted back asFloat.
A decimal Fraction has only powers of 2 and 5 as denominator, so it can be printed in decimal with a finite number of digits.
For example,
0.1 asMinimalDecimalFraction = (1/10).

Note that this does not reflect what is exactly stored in memory. For this we should use asTrueFraction.
(0.1 asTrueFraction storeStringBase: 2) = '(2r1100110011001100110011001100110011001100110011001101/2r10000000000000000000000000000000000000000000000000000000)'.

Note that asApproximateFraction might give shorter Fractions, but
- asApproximateFraction does not exclusively has power of 2 and 5 in denominator
- asApproximateFraction does not necessarily convert back exactly to the receiver

An example is:
0.11111111111 asApproximateFraction = (1/9).
0.11111111111 asMinimalDecimalFraction = (11111111111/100000000000).
But:
(1/9) asFloat storeString -> '0.1111111111111111' "has five more digits..."

A good application for such message would be:
0.111111111 asMinimalDecimalFraction asScaledDecimal = 0.111111111s9.
versus:
0.111111111 asTrueFraction asScaledDecimal = 0.11111111099999999851650755999799002893269062042236328125s56.
The later reflects the exact representation of the Float though.

=============== Diff against Kernel-eem.648 ===============

+ ----- Method: Float>>asMinimalDecimalFraction (in category 'printing') -----
+ asMinimalDecimalFraction
+ 	"Answer the shortest decimal Fraction that will equal self when converted back asFloat.
+ 	A decimal Fraction has only powers of 2 and 5 as denominator.
+ 	For example, 0.1 asMinimalDecimalFraction = (1/10)."
+
+ 	| significand exp baseExpEstimate r s mPlus mMinus scale roundingIncludesLimits d tc1 tc2 fixedFormat decPointCount slowbit shead denominator numerator |
+ 	self isFinite ifFalse: [self error: 'Only finite Float can be converted to a Fraction'].
+ 	self = 0.0 ifTrue: [^0].
+ 	self < 0.0 ifTrue: [^self negated asMinimalDecimalFraction negated].
+ 	numerator := 0.
+ 	denominator := 0.
+ 	significand := self significandAsInteger.
+ 	roundingIncludesLimits := significand even.
+ 	exp := (self exponent - 52) max: MinValLogBase2.
+ 	baseExpEstimate := (self exponent * 10 asFloat reciprocalLogBase2 - 1.0e-10) ceiling.
+ 	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]].
+ 	baseExpEstimate >= 0
+ 		ifTrue: [s := s * (10 raisedToInteger: baseExpEstimate)]
+ 		ifFalse:
+ 			[scale := 10 raisedToInteger: baseExpEstimate negated.
+ 			r := r * scale.
+ 			mPlus := mPlus * scale.
+ 			mMinus := mMinus * scale].
+ 	((r + mPlus < s) not and: [roundingIncludesLimits or: [r + mPlus > s]])
+ 		ifTrue: [baseExpEstimate := baseExpEstimate + 1]
+ 		ifFalse:
+ 			[r := r * 10.
+ 			mPlus := mPlus * 10.
+ 			mMinus := mMinus * 10].
+ 	(fixedFormat := baseExpEstimate between: -3 and: 6)
+ 		ifTrue:
+ 			[decPointCount := baseExpEstimate.
+ 			baseExpEstimate <= 0
+ 				ifTrue: [denominator := 10 raisedTo: baseExpEstimate negated]]
+ 		ifFalse:
+ 			[decPointCount := 1].
+ 	slowbit := 1 - s lowBit .
+ 	shead := s bitShift: slowbit.
+ 	[d := (r bitShift: slowbit) // shead.
+ 	r := r - (d * s).
+ 	(tc1 := (r > mMinus) not and: [roundingIncludesLimits or: [r < mMinus]]) |
+ 	(tc2 := (r + mPlus < s) not and: [roundingIncludesLimits or: [r + mPlus > s]])] whileFalse:
+ 		[numerator := 10 * numerator + d.
+ 		denominator := 10 * denominator.
+ 		r := r * 10.
+ 		mPlus := mPlus * 10.
+ 		mMinus := mMinus * 10.
+ 		decPointCount := decPointCount - 1.
+ 		decPointCount = 0 ifTrue: [denominator := 1]].
+ 	tc2 ifTrue:
+ 		[(tc1 not or: [r * 2 >= s]) ifTrue: [d := d + 1]].
+ 	numerator := 10 * numerator + d.
+ 	denominator := 10 * denominator.
+ 	decPointCount > 0
+ 		ifTrue:
+ 		[numerator := (10 raisedTo: decPointCount - 1) * numerator].
+ 	fixedFormat ifFalse:
+ 		[(baseExpEstimate - 1) > 0
+ 			ifTrue: [numerator := (10 raisedTo: baseExpEstimate - 1) * numerator]
+ 			ifFalse: [denominator := (10 raisedTo: 1 - baseExpEstimate) * (denominator max: 1)]].
+ 	denominator < 2 ifTrue: [^numerator].
+ 	^numerator / denominator!

```