How to use floating point variables and functions
If you have used earlier releases of TIGCC (prior to 0.9), you probably know that usage of floating
point values and functions was possible, but only using some very awkward syntax, which caused a lot of
headaches and nightmares. The TIGCC team (mostly
Sebastian Reichelt and
Zeljko Juric) spent a lot
of time and effort to implement native (ANSI) floats in TIGCC. And the results are now available: you now
can use floating point numbers and values regularly, as in all regular C compilers! This means
that:
-
You can now use standard ANSI types
float
, double
and
long double
without any trouble (in fact, these three types are the same,
there is no difference between them). The old-style type ti_float
still exists due to compatibility reasons, but now it is the same as float
.
-
Floating point types are automatically truncated to integers when the program
expects an integer value (for example, when a floating point variable is assigned to an integer
variable), as proposed in ANSI C. This means that you can write
n = a;
where 'n'
is an integer, and 'a'
is a float, although it is better
to express your wish more explicitely using type cast as
n = (int)a;
The function trunc which performs the
truncation explicitely still exists due to compatibility reasons.
Read further for more info about limitations of
automatic truncation.
-
To accept a floating point value from the keyboard, first accept it as a string (using
gets, some user-written input routine, or
functions from dialogs.h), then
convert the string to a floating point value using atof (ascii-to-float)
function. To get a floating point argument passed from TI-Basic to the program, use the
GetFloatArg function.
As you can see, the usage of floats with TIGCC is now essentially the same as in all other C
compilers. See the description of math.h and
timath.h header files for more info. However, floating point
support in TIGCC is not yet finished nor perfect. That's why there are still some limitations
in the use of floating point values (fortunately, they are not serious):
-
OPTIMIZE_ROM_CALLS
in combination with floats sometimes produces wrong results in calculations.
-
Floating point constant folding is not implemented yet. This means that all floating point
calculations
will be performed in the run-time, although some of them can be performed in the compile-time.
For example, if you write
b = sin (a * 2. * 3.141592654 / 180.);
all calculations will be performed during the execution of the program. In other words, the
compiler will not reduce automatically this expression to
b = sin (a * 0.03490658504);
Although you will get the same result in both cases, the first statement will produce bigger
and slower code. That's why it is highly recommended to manually re-express any expression in
which some calculations may be performed apriori, like in the example given above. The following
expression will generate even worse code (although it will produce the correct result too):
b = sin (a * 2 * 3.141592654 / 180);
Read the next point to see why.
-
The promotion of unsigned long integers which are too big to fit in a long integer type
(i.e. values between 2147483648 and 4294967295) does not work correctly (the promoted value
will be negative). This means that
float a = 4000000000;
will not produce a correct result, although
float a = 4000000000.;
will. This is due to an invalid promotion in the first case (there is no promotion in the
second case). In the first case, you will eventually get a warning message
"decimal constant is so large that it is unsigned", if you didn't use the 'UL'
suffix explicitely. This is a fortune (although it is not very likely that you will ever have
any problems with above limitation).
-
The promotion of double long integers (long long types) to
floating point values, and truncation of floating point values to longlongs are not implemented
yet (but will be in the near future). If you try to do this (which is not very likely), you
will get an "undefined reference" error during the linking stage.
As an example of usage of floating point values and functions, the program given below
reads coefficients of a quadratic equation from the keyboard, then calculates and displays
the solutions of the equation (including complex ones):
#define SAVE_SCREEN
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <kbd.h>
int _ti89, _ti92plus;
void _main (void)
{
float a, b, c, d;
char buffer[200];
clrscr ();
puts ("a=");
a = atof (gets (buffer));
puts ("b=");
b = atof (gets (buffer));
puts ("c=");
c = atof (gets (buffer));
if (is_nan (a) || is_nan (b) || is_nan (c)) return;
d = b * b - 4. * a * c;
if (d >= 0.)
{
float x1, x2;
x1 = (-b + sqrt (d)) / (2. * a);
x2 = (-b - sqrt (d)) / (2. * a);
printf ("\nx1=%f\nx2=%f", x1, x2);
}
else
{
float re, im;
re = -b / (2. * a);
im = fabs (sqrt (-d) / (2. * a));
printf ("\nx1=%f-%f*i\nx2=%f+%f*i", re, im, re, im);
}
ngetchx();
}
See the description of included header files for more info about used commands.
As already mentioned above, the new floating point support is implemented without
losing compatibility with programs written with releases of TIGCC before 0.9
(more precise, the degree of compatibility is about 95%; read further to see possible
reasons of incompatibility). So, the quadratic equation solver given bellow, which
is written using old methods (prior to TIGCC 0.9), will still work with a new compiler.
Compare this (old-style) code with the previous (new-style) one to see how much the
new-style code is clearer...
#define SAVE_SCREEN
#include <stdio.h>
#include <timath.h>
#include <string.h>
#include <kbd.h>
int _ti89, _ti92plus;
void _main (void)
{
ti_float a, b, c, d;
char buffer[200];
clrscr ();
puts ("a=");
a = atof (gets (buffer));
puts ("b=");
b = atof (gets (buffer));
puts ("c=");
c = atof (gets (buffer));
if (is_nan (a) || is_nan (b) || is_nan (c)) return;
d = fsub (fmul (b, b), fmul (FLT (4), fmul (a, c)));
if (fcmp (d, ZERO) >= 0)
{
ti_float x1, x2;
x1 = fdiv (fadd (fneg (b), sqrt (d)), fadd (a, a));
x2 = fdiv (fsub (fneg (b), sqrt (d)), fadd (a, a));
printf ("\nx1=%f\nx2=%f", x1, x2);
}
else
{
ti_float re, im;
re = fdiv (fneg (b), fadd (a,a));
im = fabs (fdiv (sqrt (fneg (d)), fadd(a, a)));
printf ("\nx1=%f-%f*i\nx2=%f+%f*i", re, im, re, im);
}
ngetchx();
}
The possible reasons which may cause incompatibility (very unlikely) with programs written
with older versions of TIGCC (prior to 0.9) are:
-
The types ti_float and bcd are not the same any more.
ti_float
is now equal to ANSI type float
, but
bcd
is still a structure. If your program uses the
bcd
type (not very likely), you should probably change it to
float
to make the program working, because functions which expect a
float
type will not accept a structured type. Two macros called
float_to_bcd and
bcd_to_float are introduced to provide more general
conversion when necessary.
-
Functions fadd, fsub,
fmul, fdiv,
fneg, fcmp,
flt and trunc are not
absolutely equal to functions
bcdadd, bcdsub,
bcdmul, fdiv,
bcdneg, fcmp,
bcdbcd and bcdlong any more.
The first group of functions now works with the ordinary
float
type (and they
will continue to work with ti_float), but the second
group now only works with bcd structures. So, if you used
bcdadd
etc. in your program (not very likely), you should
probably change it to fadd
etc. to make the program working.