[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [sc-users] Conversion decimal into fractional



Here's a better hack for you. It represents the
numbers internally as floats (because it's silly to do
otherwise), but this class displays its value as a
fraction, unless the value is irrational.

Doing math on instances of this class will be slower,
because of the extra dispatches involved.

I've now spent as much time as I can afford on this
problem. I will leave it as the proverbial "exercise
for the reader" to add additional features (such as, a
method to get the numerator only, or the denominator,
or isRational which would output true if a denominator
can be found).

1 /% 2  // alternate division operator to create a
frac
1/2

a = Array.fill(8, { |i| (i+4) /% (i+3) });
[ 4/3, 5/4, 6/5, 7/6, 8/7, 9/8, 10/9, 11/10 ]

a.sum
23761/2520

a.sum.asFloat
9.4289682539683

// regular division produces decimals
b = b = Array.fill(8, { |i| (i+4) / (i+3) });
[ 1.3333333333333, 1.25, 1.2, 1.1666666666667,
1.1428571428571, 1.125, 1.1111111111111, 1.1 ]

b.asRational  // convert to fractions
[ 4/3, 5/4, 6/5, 7/6, 8/7, 9/8, 10/9, 11/10 ]

a * 2
[ 8/3, 5/2, 12/5, 7/3, 16/7, 9/4, 20/9, 11/5 ]

a + 0.45
[ 107/60, 17/10, 33/20, 97/60, 223/140, 63/40,
281/180, 31/20 ]

b = a + sqrt(2)  // don't estimate frac for
irrationals
[ 2.7475468957064, 2.6642135623731, 2.6142135623731,
2.5808802290398, 2.5570707052302, 2.5392135623731,
2.5253246734842, 2.5142135623731 ]

c = b - sqrt(2)  // but the class remains Rational
[ 4/3, 5/4, 6/5, 7/6, 8/7, 9/8, 10/9, 11/10 ]

1.0245.asRational
2049/2000

// evaluating this string results in the original
// Array of Rationals
a.asCompileString
[ 4/%3, 5/%4, 6/%5, 7/%6, 8/%7, 9/%8, 10/%9, 11/%10 ]

hjh

=====
____   James Harkins /// dewdrop world
\  /   jamshark70@xxxxxxxxx
 \/    http://www.dewdrop-world.net

"... love and hot pants, peace, harmony..."
  -- Dick Lee, Hot Pants: The Musical
+ SimpleNumber {
	asRational { ^Rational.new(this) }

	/% { |that| ^Rational(this.asFloat / that) }
}

+ Float {
	/% { |that| ^Rational(this / that) }
}

+ Collection {
	asRational {
		^this.collect({ |item| item.asRational })
	}
}
Rational : AbstractFunction {
	var <value;

	*new { |value| ^super.new.value_(value) }

	*newFrom { |value| ^super.new.value_(value) }

	value_ { |v| v.isNumber.if({ value = v },
		{ MethodError("Value of a Rational must be a number", this).throw })
	}

	composeUnaryOp { arg aSelector;
		^value.perform(aSelector).asRational
	}
	composeBinaryOp { arg aSelector, operand, adverb;
		^value.perform(aSelector, operand, adverb).asRational
	}
	reverseComposeBinaryOp { arg aSelector, something, adverb;
		^something.perform(aSelector, value, adverb).asRational
	}
	composeNAryOp { arg aSelector, anArgList;
		^value.perform(aSelector, *anArgList).asRational
	}

	asRational { ^this }
	asFloat { ^value }
	asInteger { ^value.asInteger }

	asString {
		var	denom;
		((denom = value.fuzzygcd(1, 0.0001, 100)).notNil
			and: { denom != 1 })
		.if({
			^(value * (denom = denom.reciprocal.round)).asString ++ "/" ++ denom
		}, {
			^value.asString
		});
	}

	asCompileString {
		var	denom;
		((denom = value.fuzzygcd(1, 0.0001, 100)).notNil
			and: { denom != 1 })
		.if({
			^(value * (denom = denom.reciprocal.round)).asString ++ "/%" ++ denom
		}, {
			^"Rational(" ++ value.asCompileString ++ ")"
		});
	}

	printOn { |stream| stream << this.asString }

	storeOn { |stream| stream << this.asCompileString }

}