libmawk numerics
Mawk implemented all numeric calculations using double precision floating
point numbers. Libmawk has a compile-time option for using different
types for numerics, with the following choices available:
handling exceptions and NaNs
Mawk was geared towards catching floating point errors (such as
division by zero or log(-1)) and report a runtime error as soon as
possible. Libmawk, targeting embedded script application, should minimize
runtime errors by providing means for the script to check for them and
recover.
There are multiple approaches for handling suc errors. A design decision
has been made for exclusively using NaN (Not a Number) - on platforms where it is not
implemented by the FPU or by libc, it's emulated. NaN support shall work
the same way for all available numeric types.
Rules about NaN are few and simple:
- 1. NaN should be a sepcial value that can not be mistaken for a valid number by the script
- 2. math library calls (e.g. log()) should return NaN for invalid input; library calls shall accept NaN as input and return NaN as output
- 3. other library functions should handle NaN properly (i.e. printf %f or print shall write "nan" and a string-to-number conversion shall be able to understand "nan")
- 4. if any input of a calculation is NaN, the result shall be NaN (e.g. NaN+1 is NaN); such calculations never cause runtime error
- 5. 1/0, 0/0 and similar corner cases are all NaN - there is no inf
- 6. the only runtime error that may be caused by NaNs is when a conditional jump depends on a NaN (e.g. if (nan) {})
- 7. isnan(x) returns 1 if x is NaN.
In practice this means a block of numeric calculations can be done safely,
without checking any input or intermediate results. At the end of the
block the result(s) shall be checked with isnan(). As long as the results
(even indirectly) depend on an input or intermediate result that is NaN,
the result is guaranteed to be NaN too, without the risk of a runtime error.
Implementation
type: double
If the system has FPE, it's disabled. If the system does not have native
NaN, a special NaN value is defined (using HUGE_VAL) and
before each operation all inputs are checked for NaN to make sure the
output is NaN too. On systems with NaN
### TODO: inf? ###
type: int
### TODO: ###
division by zero
Before divisions input is always checked and division by zero is
replaced by a NaN. The same mechanism is in place for all numeric types.