Proposal for modifying gnucash to use exact quantities

Steven Murdoch sjmurdoch@linuxfan.com
Tue, 01 Aug 2000 16:36:56 +0100


I think now would be a good time to more explicitly state a few ideas
about my last suggestion.

The reason I have done this is to allow values to be represented in
integers. This will have several other benefits I said in my previous
mail, but the more I think of it, he more I think the speed increase
will be. For example the task of comparing two integers is trivial O(1)
complexity, but comparing two rationals is far from it and I think has
O(sqr_root(n)) complexity, so if this is done several times the
performance difference will be noticeable

It also represents the concept clearer. Calculations are done in SCU's
when converting to major currencies this is more an issue of display
(number of mills in a dollar has no significance in calculations), and
is also a property of the commodity type, rather than the value. To put
it in more concrete terms it is a proper of a column in the register
window, rather than a cell.

This also gives an additional advantage, it will not allow a mixture of
'denominators' in a type. This could by undesirable in certain cases (in
which case Bills rational representation should be used) but in other
cases it is highly desirable as it removes the possibility of having
64ths of a dollar, when this is not a valid value (say for a bank
balance).
In general the integer representation would be better for quantities
that have the property that between two distinct values there is a
finite number of other values. Examples would be number of shares (pure
integer), amount of currency (integer SCU's), bank balance (integer
SCU's) etc...
Basically any quantity that can be represented as an integer should be
represented as an integer.

This is a a first sketch of how I would represent a class of values
(very similar to Bills proposal, but they do represent different things)

struct gnc_integer_commodity {
	char *fullname,
	char *mnemonic,
	char *namespace,
	char *currency_symbol,
	guint32 scus_per_major_unit
	}; =


The actual values would be stored as integers (say guint64) and
importantly the value would be in SCU's

A few examples could be (not in any particular language):
quantity=3Dgnc_integer_commodity_create("Quantity","Q","Internal",NULL,1)=

number_of_my_shares=3D231 #means I have 231 shares

dollars=3Dgnc_integer_commodity_create("Dollars","USD","Currency","$",1_0=
00)
my_balance=3D12340 #means I have USD $12.34

Share_Price=3Dgnc_integer_commodity_create("Pounds","GBP","Currency","=A3=
",400)
price_of_a_share=3D183 #this share is worth 45_3/4 pence

Pounds=3Dgnc_integer_commodity_create("Pounds","GBP","Currency","=A3",100=
)

GBP_vs_USD=3Dgnc_integer_commodity_create("Pounds vs
Dollars","GBP-USD","Exchange Rate",NULL,1_000_000)
current_rate=3D666178 #todays exchange rate
# I think a note is needed here. On the Forex exchange rates are quoted
and calculated as fixed decimals. The number of decimal places depends
on which pair of currencies, but is fixed for a given pair so can be
represented as an integer.

The operations that will be used almost all the time are trivial
(addition, subtraction, multiplication within a type, comparison) but
share transactions and currency conversions deserve more explanation.

guint64 transaction_total(guint64 quantity,
			guint64 price,
			guint32 scu_per_major_unit_in
			guint32 scu_per_major_unit_out
			char rounding)
where transaction_total=3Dround((quantity*price*scu_out)/scu_in)

As an example I'll use the complicated case of a 231 London shares
(quoted in 1/4 pence, say 45_3/4) but the transaction total is billed in
pence (rounded up)

share_transaction_total(231,183,400,100,GNC_RND_CEIL)=3Dceil((231*183*100=
)/400)

note the division is done at the end, so there will be no cumulative
error. The implementation of this is not quite trivial, but is is
efficient and exact. I have not implemented it as it is a special case
of one of Bills functions.

For currency conversion I'll use the example of converting USD 12.31 to
GBP

guint64 currency_convert(guint64 in,
			guint32 scu_per_major_unit_in,
			guint64 rate,
			guint32 scu_per_major_unit_rate,
			guint32 scu_per_major_unit_out)

where currency_convert=3Dround((in*rate*scu_out)/(scu_in*scu_out))
currency_convert(12310,1000,666178,1_000_000,100,GNC_RND_CEIL)=3Dceil((12=
310*666178*100)/(1000*1e6))

The same goes for this as I said for share calculations (share
calculations are in fact a special case of the function above).

The last two functions are quite similar to Bills, so you may ask the
question what's the point of adding a new type to gnc_numeric. One
possible answer is efficiency; integer functions are far faster than
rational ones and the majority of calculations are within type and only
very rare cases currency_convert or transaction_total will be used. Most
book keeping is done within a currency and this will be dramatically
faster. Also the storage required is about half the size, so decreasing
memory usage, disk space and more importantly disk transfer time


I'll end this by saying the purpose of the API a purely for discussion
starter and is not intended to b immediately implemented, nor is it set
in stone - far from it. I have also been free on the syntax, but if
there is confusion over what I mean then please tell me and I'll be more
precise.

-- =

Steven Murdoch.
email: sjmurdoch@linuxfan.com
web: http://www.bigfoot.com/~murdomania/
PGP Keys: http://www.bigfoot.com/~murdomania/contact.htm
Geek Code: http://www.bigfoot.com/~murdomania/geek.htm