My implementation of **fx** uses these two C structures:

#define IntSize long long
struct frac
{
IntSize num;
IntSize den;
};
struct fx
{
struct frac base;
struct frac exp;
} xx;

(IntSize is equated here with type long long, giving it 64-bit precision on most target machines. Its definition could be altered to attain greater or more portable precision, depending upon the developer's needs.)

All **fx** numbers take the form struct fx, which is to say, a fractional (struct frac) base number with a fractional exponent. All four components are IntSize integers. Where the **fx** is itself an integer or whole number, all but the xx.base.num is 1 (one). So, for example, the whole number 142 would take the form

xx.base.num = 142;
xx.base.den = 1;
xx.exp.num = 1;
xx.exp.den = 1;

Which is

(142 / 1) ^ (1 / 1)

*The quantity 142 divided by 1, raised to the power of the quantity of 1 divided by 1*

Fractions

An example of a simple fractional value might be

(1 / 3) ^ (1 / 1)

There is no repeating decimal .3333 and no risk of rounding errors. Indeed, there is no attempt to carry out the division operation. The fraction is simply stored as a numerator / denominator fraction, all components still IntSize integers.

For normal decimal point numbers, the **fx** number begins to resemble Cobol's implied decimal concept. xx.base.den is always some power of ten, depending upon the number of implied decimals. In a business application in which there are dollar & cents fields, the denominator would be 100. For example, the dollar amount $547.95 would be

(54795 / 100) ^ (1 / 1)

If IntSize is a 64-bit integer, the **fx** number could represent a decimal number with 18 significant digits (as in Cobol), before or after the implied decimal point. This translates to dollar / cents amounts in the range of about a thousand trillion dollars without ever losing a single cent to rounding errors or floating point inaccuracies. That's (almost?) enough to keep track of the entire US national debt, down to the last penny. If the developer needs a greater range than that, define IntSize as 128 bits, or whatever the target machine will allow.

Exponents

The second half of the **fx** number is the exponent, or power to which the base fraction is raised. It is also a fractional number, so that fractional powers (roots) may be represented. So, for example, thirteen cubed would be

xx.base.num = 13;
xx.base.den = 1;
xx.exp.num = 3;
xx.exp.den = 1;

Which is

(13 / 1) ^ (3 / 1)

The square root of .7 would be

(7 / 10) ^ (1 / 2)

As with base fractions, there is no attempt to carry out the power operation. The exponent is simply stored as two IntSize integers.

Imaginary Numbers

A unique feature of the **fx** technique is the ability to handle imaginary numbers, i.e. numbers based on **i**, the square root of negative one.

(-1 / 1) ^ (1 / 2)

The

**fx** number at left could be defined in terms of

**i** as the complex number

((5^(1/2))*i)^(7/2)

but to do anything with it in C would require an extension to C's conventional math library, and would still involve floating point math with its inherent inaccuracies. (Plus, I still wouldn't know how to write the C code.)

You learned this in high school. Any negative number with an even root is an imaginary number. This **fx** number, for example:
(-5 / 1) ^ (7 / 4)

is impossible to resolve within the domain of real numbers. But normal math rules still apply. For example, two imaginary numbers can be multiplied together:

(-5 / 1) ^ (7 / 4)
* (-5 / 1) ^ (5 / 4)
-------------------
(-5 / 1) ^ (12/ 4)

The product, in this case, no longer has an even root (since the exponent can be simplified to the integer 3), and so can be resolved as the real number -125.

The point is, the **fx** technique readily facilitates the above and other computations involving imaginary numbers. I wouldn't know how to do it using floating point math or any other conventional programming method.

Limitations

The first limitation is that transcendental numbers such as pi, log, and trig functions cannot be represented by **fx** numbers.

Secondly, as noted above, the maximum capacity of the **fx** number is limited by the definition of its IntSize element. Some calculations may yield results beyond this capacity. When this happens, the **fx** calculation will explicitly fail rather than give inaccurate results.

Well then, you can be sure that your compound 4-part **fx** number is exact. But at some point you may want your application to resolve this compound **fx** number to a single numeric value, expressed as an integer, simple fraction, or number with fixed decimal point. In many cases, this will be impossible without reverting to floating point or arbitrary precision math, and the possibility of rounding errors and inaccuracies. The astute developer will keep this to a minimum by performing all calculations and storing all interim values as **fx** numbers, waiting until, say, printing a report to display the resolved numeric value.

Implementation