| Using Floating Point Arithmetic

If you have used earlier releases of TIGCC (prior to 0.9), you probably know that the use 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 implementing native (ANSI) floats in TIGCC. And the results are now available: you can use regular floating point numbers and values now, as in all regular C compilers! This means that:

- You can now use the 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`.

- You can now use "standard" floating point constants (like 2.854). So, the following is now valid:

      float a, b, c;
      a = 2.854;
      b = 7;
      c = 1.5e-7;

  In other words, no more ugly FLT and FEXP macros! They still exist due to compatibility reasons, but their usage is very deprecated now.

- You can now use standard arithmetic ('+', '-', '*', '/) and comparison ('<', '>', '<=', '>=', '==' and '!=') operators with floating point values. There is no need any more for the use of awkward functions like fadd, fcmp etc. So statements like these are now accepted:

      p = (q + 3.15) / (q - r);
      x1 = (-b + sqrt (b * b - 4 * a * c)) / (2 * a);
      if (p < 3.0) q += 1e-3;

  This feature is probably the best present for TIGCC users...

- You can use all floating point functions (like sqrt, sin etc.) as usual. In this release, a standard ANSI math library math.h is implemented, which contains nearly all functions proposed by ANSI C, with some extensions. The old timath.h library file still exists due to compatibility reasons, and it contains more functions than math.h, because it contains some functions for making compatibility with older programs, and some functions which are internally related to the TIOS. Anyway, you can do calculations like these:

      a = sin (b);
      b = exp (1);
      c = log (3.19);
      a = sqrt (b + c) * sinh (a / 2.);

  As you can see from the second example, automatic promotion from integer constants to the floating point is also implemented.

- Integer types are now automatically promoted to floating point values when necessary, as proposed in ANSI C. This means that the following constructions are valid, assuming that `'n'` is a variable of integer (short, long, signed, unsigned) type:

      a = n;
      b = sqrt (n);
      c = b + n;

  You can also explicitely cast an integer value to a floating point one:

      a = (float)n;

  The function flt which performs the promotion explicitely still exists, due to compatibility reasons.

- 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.

- You can use printf and similar functions to print floating point values, using the format specifiers `%f`, `%e` and `%g`, as defined in ANSI C. So, the following is completely legal:

      printf ("sin(%f)=%f", a, sin(a));
      printf ("%f", 3.14);

  Note that the second statement was not valid prior to release 0.9 of TIGCC!

- 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 the 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 the math.h and timath.h header files for more info. However, floating point support in TIGCC is not perfect yet. That's why there are still some limitations in the use of floating point values (fortunately, they are not serious):

- 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.

- 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. 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 (called "Float Test") reads coefficients of a quadratic equation from the keyboard, then calculates and displays the solutions of the equation (including complex ones):

    #define USE_TI89
    #define USE_TI92PLUS
    #define USE_V200

    #define SAVE_SCREEN

    #include <stdio.h>
    #include <math.h>
    #include <string.h>
    #include <kbd.h>

    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 the included header files for more info about the functions used.
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 below, 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 clearer the new-style code is...

    #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 work, because functions which expect a `float` type will not accept a structured type. Two macros called float_to_bcd and bcd_to_float have been introduced to provide more general conversion if necessary.

- Functions fadd, fsub, fmul, fdiv, fneg, fcmp, flt and trunc are not absolutely equal to functions bcdadd, bcdsub, bcdmul, bcddiv, bcdneg, bcdcmp, 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 work.

- Suppose that your old program uses the ti_float type with direct access to its internal fields (not very likely), like in the following example:

      ti_float a;
      a.exponent = 0x4003;
      a.mantissa = 0x3284300000000000;

  Then the program will not work with the new compiler, because `ti_float` is not a structure any more. Using a new macro bcd_var you can easily get your program to work. All you need to do is to re-express the above statements as

      ti_float a;
      bcd_var(a).exponent = 0x4003;
      bcd_var(a).mantissa = 0x3284300000000000;

  Note, however, that the bcd type is still a structure.