fraclib.e

new topic     » topic index » view thread      » older message » newer message

I'm wrote this very simple lib, which gives infinite precision for rational
numbers by storing numbers as fractions, i.e. "1/3 * 3" (actually,
"f_mul({1,3}, {3,1})" using my lib) returns 1, rather than 0.9999999999

It stores fractions as a 2 element biginteger sequence. (A biginteger
is currently an atom thats restricted to whole numbers, I eventually plan
to add a bignum library to it, so you are not limited by the +/-1e300
size of an atom.)

If you try out this line, "? atom_to_f(f_to_atom({1,3}))", via my lib,
you'll find it takes a VERY long time to compute. This is because my
reduce() function is not very optimized. Any one have any ideas on how it
could be made faster?

TIA,
jbrown

My lib is pasted into my signature space at the bottom of this email.

-- 
include get.e
include misc.e

global type biginteger(object a)
	if not atom(a) then
		return 0
	end if
	if floor(a) != a then
		return 0
	end if
	return 1
end type

global type fraction(object s)
	if not sequence(s) then
		return 0
	end if
	if length(s) != 2 then
		return 0
	end if
	if not(biginteger(s[1]) and biginteger(s[2])) then
		return 0
	end if
	return 1
end type

function gcd(biginteger num1, biginteger num2)
	biginteger gcd_val
	if num1 > num2 then
		gcd_val = num1
	else
		gcd_val = num2
	end if
	while remainder(num1, gcd_val) != 0
			or remainder(num2, gcd_val) != 0 do
		gcd_val -= 1
	end while
	return gcd_val
end function
function reduce(fraction f)
	if f[2] = 0 then
		f[2] /= 0 --trigger an error here.
	end if
	return f / gcd(f[1], f[2])
end function

global function f_mul(fraction f1, fraction f2)
	return reduce({f1[1]*f2[1], f1[2]*f2[2]})
end function

global function f_div(fraction f1, fraction f2)
	return f_mul(f1, {f2[2], f2[1]})
end function

global function f_add(fraction f1, fraction f2)
	biginteger f1_dem, f2_dem
	f1_dem = f1[2]
	f2_dem = f2[2]
	f1 *= f2_dem
	f2 *= f1_dem
	return reduce({f1[1]+f2[1], f1[2]})
end function

global function f_sub(fraction f1, fraction f2)
	biginteger f1_dem, f2_dem
	f1_dem = f1[2]
	f2_dem = f2[2]
	f1 *= f2_dem
	f2 *= f1_dem
	return reduce({f1[1]-f2[1], f1[2]})
end function

global function f_to_atom(fraction f)
	return f[1]/f[2]
end function

global function f_to_string(fraction f)
	return sprintf("%d/%d", f)
end function

global function string_to_f(sequence s)
	--if we had a scanf(), i could just get it via scanf("%d/%d")! sheesh!
	sequence v
	integer i
	i = find('/', s)
	if i = 0 then
		return ""
	end if
	v = {0,0}
	v[1] = value(s[1..i-1])
	v[2] = value(s[i+1..length(s)])
	if v[1][1] !=0 or v[2][1] != 0 then
		return ""
	end if
	v[1] = v[1][2]
	v[2] = v[2][2]
	if not fraction(v) then
		return ""
	end if
	return v
end function

global function atom_to_f(atom a)
	sequence s, t
	integer i, si
	--base_dem is the 1 followed by zeros,
	--base_num is the decimal part as a numerator,
	--whole_num should be obvious (whole number part of a)
	biginteger base_dem, base_num, whole_num
	if biginteger(a) then
		--ah, the easy part ;]
		return {a, 1}
	else
		--aw, the hard part :[
		s = sprint(a)
		if s[1] = '-' then
			--preserve the sign in case of '-0.'
			si = -1
			s = s[2..length(s)]
		else
			si = 1
		end if
		i = find('.', s)
		t = value(s[1..i-1])
		if t[1] = 0 then
			whole_num = t[2]
		else
			return 0
		end if
		t = value(s[i+1..length(s)])
		if t[1] = 0 then
			base_num = t[2]
		else
			return 0
		end if
		t = value("1"&repeat('0', length(s[i+1..length(s)])))
		if t[1] = 0 then
			base_dem = t[2]
		else
			return 0
		end if
		--guarrentted not to be zero unless a is, so we can safely put it here.
		--(bases_dem works as well in theory, but I didnt test it and I think
		--that its a bad idea since the numerator should have the sign in
		--any case, as I understand basic math that is.)
		base_num *= si
		return f_add({whole_num, 1}, {base_num, base_dem})
	end if
end function

new topic     » topic index » view thread      » older message » newer message

Search



Quick Links

User menu

Not signed in.

Misc Menu