JTC1/SC22/WG14
N753
* Document Number: WG14 N753/J11 97-116
C9X Revision Proposal
=====================
* Title: LIA-1 Binding: Rationale
Author: Fred J. Tydeman
Author Affiliation: Tydeman Consulting
Postal Address: 3711 Del Robles Dr., Austin, Texas, USA, 78727
E-mail Address: [email protected]
Telephone Number: +1 (512) 255-8696
Fax Number: +1 (512) 255-8696
Sponsor: WG14
Date: 1997-09-21
Proposal Category:
Y_ Editorial change/non-normative contribution
__ Correction
__ New feature
__ Addition to obsolescent feature list
__ Addition to Future Directions
__ Other (please specify) ______________________________
Area of Standard Affected:
__ Environment
__ Language
__ Preprocessor
__ Library
__ Macro/typedef/tag name
__ Function
__ Header
Y_ Other (please specify) Rational______________________
Prior Art: C89.
Target Audience: Programmers writing programs that perform a
significant amount of numeric processing.___________________
Related Documents (if any):
WG14/N758 C9X and LIA-1 informative annex,
WG14/N756 LIA-1 Binding: Arithmetic exception => SIGFPE,
WG14/N755 LIA-1 Binding: <fenv.h> to <stdmath.h>,
WG14/N752 LIA-1 Binding: Optional parts annex,
WG14/N751 LIA-1 Binding: Combined LIA-1 + IEC-559 annex,
WG14/N750 LIA-1 Binding: LIA-1 annex,
WG14/N749 LIA-1 Binding: <stdlia.h>,
WG14/N748 LIA-1 Binding: Adding 'pole' from LIA-2,
WG14/N747 IEC 559 Binding: Signaling NaNs,
WG14/N528 C Binding for LIA-1,
WG14/N488 LIA-2 (math library),
WG14/N487 LIA-1 (arithmetic),
WG14/N486 LIA Overview,
WG14/N463 Impact of adding LIA-1,
WG14/N461 C Binding of LIA-1,
NCEG 91-050 Better Environmental Inquiries for All Approximate
Arithmetics
NCEG 91-022 The ANSI C Language and the LCAS (Revision 1),
NCEG 91-017 The ANSI C Language and the LCAS [LIA-1]
Proposal Attached: _Y Yes __ No, but what's your interest?
Abstract: This is a list of open issues (to be resolved by
the committee) followed by the Rational for the addition of
LIA-1 to C9X. As issues are resolved, they will be turned
into rational.
Proposal:
Note: The '*' characters in the lefthand column are not part
of the proposal (they are useful for emacs M-x outline mode)
In the following, bold text, italic text,
<TT>code sample</TT> are the conventions used to indicate
text different from normal.
'OPEN' in the lefthand column means the issue is still to be
resolved.
* Overview
It appears that the minimum that an implementation needs to
do (with respect to code generation) to support LIA is:
Define modulo (INT_OUT_OF_BOUNDS) to be 1 (wrap) => no need
to detect signed integer overflow.
Detect integer divide by zero and invalid.
Detect floating underflow, overflow, divide by zero, invalid.
When an exception happens:
Either
Set indicators and proceed with a continuation value.
Invoke a function similar to __lia_indicators() at
normal program termination.
Or
Print a "hard to ignore" message and terminate.
Both alternatives for notification must be supported and
the user must be able to choose which alternative to use.
Since exceptions must be detected and acted upon, and we
have heard complaints from some implementors that (integer)
exception detection is hard to do, a fundamental question
that must be answered is: In the long term, is the proposed
Annex H normative, conditional normative or informative? In
the short term (the committee draft of this November '97),
it is written up as an informative annex of how C9X meets
LIA-1. In the long term (final committee draft of June '98
or an amendment later), it is written up as conditional
normative meaning if an implementor supports LIA, then it
must be done as per that annex, but implementors are not
required to support LIA-1. This is the same as annex F for
IEC 559.
OPEN A more basic question is: Do we need LIA? What market wants
LIA? Are there users requesting that implementors add
support for LIA? What problem does LIA solve?
--
OPEN Will we allow partial conformity (Annex B in LIA-1)? If so,
we would allow an implementation to claim conformance, but
they would indicate how they fall short with the following
macros:
LIA_STRICT
SILENT_UNDERFLOW
COMPARISON_VIA_SUBTRACT
NEGATE_MAY_FAIL
* Exceptions / traps / signals / interrupts
LIA-1 requires that arithmetic exceptions be detected.
C requires that signal handlers honor SIGFPE.
C requires signal handlers can terminate via longjmp.
What is not required (by C or LIA-1) is that arithmetic
exceptions (in trap mode) map into the generation of SIGFPE
(or any other SIG*).
OPEN Should we require that arithmetic exceptions (that trap)
raise SIGFPE? Only for LIA-1 compliance? Optional in
non-LIA-1 mode?
Should there be a unique SIG* for each exception?
Once we tie arithmetic exceptions to SIGFPE (or equivalent),
then C has a useful trap handling mechanism.
The basic proposal is written up assuming detect, trap and
terminate is implementation defined (no mention of SIGFPE)
how it is done.
The mapping of exceptions that trap get turned into SIGFPE
is a separate proposal.
--
Should we define syntax/semantics in Annex J for optional
IEEE-754 trap functions: fesettraphandler(),
fesavetraphandler(), ferestoretraphandler(),
fependingtraps() or similar spelling?
signal( SIGFPE, function ) is the equivalent of set trap
handler. signal( SIGFPE, SIG_IGN ) is one way to ignore
(disable) traps. signal also can do save/restore of trap
handler.
--
OPEN Do we need concept of raised exceptions as distinct from
just set and clear? 7.6.2.4 fesetexcept() and 7.6.4.3
fesetenv() say that they do not raise exceptions, but only
set the state of the flags. 7.6.4.4 feupdateenv() says it
saves current [raised?] exceptions ... and then raises the
saved exceptions.
Since we now (optionally) have the ability to enable/disable
traps for exceptions, raised versus set matters (without
traps, it does not matter). A status flag starts off clear.
An arithmetic operation fails (or feraiseexcept() is done)
to raise the exception (what is status flag?). If no trap
happens (trap is disabled), then the (sticky) status flag is
set (and a continuation value is used), else a trap happens
(and that state of the status flag is undefined).
Once we have raised exceptions and trap handlers, we then
have current and pending exceptions (as seen from the trap
handler).
It is assumed that there is a trap enable/disable control
word. Trap enablement is boolean: disabled, enabled.
I do not understand the model of exceptions in the current
C9X draft. Some possible models are:
1st model: one set of status flags that have three states:
clear, raised, set.
2nd model: two sets of status flags:
sticky flags are boolean: clear, set.
exceptions are boolean: clear, raised.
3rd model: one set of status flags that are boolean: clear,
set. But, now the trap enable/disable control word enters
the picture. Status flag set AND trap enabled means raised
(and trap taken). With this model, the only way for
fesetexcept() to set a flag and not raise an exception is to
disable the trap.
--
How do we do "Hard to ignore" indicators message at
termination?
Print to stderr is the suggested way.
--
OPEN How best should sample atexit to print indicators at program
termination be made normative?
A suggested method is to register a function during program
startup (as if by the use of atexit()), so that upon program
termination, it prints out to stderr the indicators
still set.
During program startup, the equivalent of
signal(SIGFPE, SIG_IGN);
fedisabletrap( FE_TRAP_INVALID | FE_TRAP_DIVBYZERO
| FE_TRAP_OVERFLOW | FE_TRAP_UNDERFLOW );
iedisabletrap( INT_TRAP_INVALID | INT_TRAP_DIVBYZERO
| INT_TRAP_OVERFLOW );
atexit( __lia_indicators );
shall be executed.
<TT>
void __lia_indicators(void){ /* during program termination */
int fp_flags = fetestexcept(FE_ALL_EXCEPT);
int int_flags = ietestexcept(INT_ALL_EXCEPT);
int fail = 0;
if( (fp_flags & FE_INVALID) || (int_flags & INT_INVALID) ){
(void)fprintf(stderr, "LIA: Undefined set");
fail = 1;
}
if( (fp_flags & FE_DIVBYZERO) || (int_flags & INT_DIVBYZERO) ){
(void)fprintf(stderr, "LIA: Pole set");
fail = 1;
}
if( int_flags & INT_OVERFLOW ){
(void)fprintf(stderr, "LIA: Integer overflow set");
fail = 1;
}
if( fp_flags & FE_OVERFLOW ){
(void)fprintf(stderr, "LIA: Float overflow set");
fail = 1;
}
if( fp_flags & FE_UNDERFLOW ){
(void)fprintf(stderr, "LIA: Float underflow set");
fail = 1;
}
if( fail ){
exit(EXIT_FAILURE); /* unsuccessful completion */
}
};
</TT>
--
What about errno? Domain and range errors can still happen.
C9X (independent of LIA-1) no longer requires EDOM and
ERANGE to be assigned to errno.
--
OPEN IEC 559 says: "The invalid operations are ... Conversion of
a binary floating-point number to an integer or decimal
format when overflow, infinity, or NaN precludes a faithful
representation in that format and this cannot otherwise be
signaled." Integer overflow can be otherwise signaled, so
it should be INT_OVERFLOW for "fp large -> int", but may be
FE_INVALID. Integer invalid can be otherwise signaled, so
it should be INT_INVALID for "fp inf -> int" and "fp NaN ->
int", but may be FE_INVALID. Therefore, conversion from
floating-point to integral is implementation defined via
FP2INT_OF_LARGE. Reason: Many existing hardware
implementations of IEC 559 raise floating-point invalid for
these bad conversions and an exception is raised, just not
the one LIA-1 wants. Counter reason: make it a slow
function call to do what LIA-1 requires, instead of using
the fast hardware.
Should FP2INT_OF_* be in <stdlia.h> or <fenv.h>? Assume
<stdlia.h>.
--
OPEN Should fedisabletrap() return void (like feclearexcept), a
boolean indicator (like fesetround), or the set of
indicators that it supports? Same question for
feenabletrap().
--
Should the ie*trap() and fe*trap() functions be merged into
one set of (enable/disable/test)_trap() functions, along
with FE_TRAP_* and INT_TRAP_* macros? For a minimal support
of trapping (what is being proposed), there should be no
problem having one set of functions. But, if the standard
or implementors were to add additional functions, similar to
fegetexceptflag, but for traps, then the representation of
floating-point traps might differ enough from integer traps
to make implementation difficult or impossible. So, they
are separate for now.
* Packaging
General name space pollution. There are many macros and
functions that could conflict with existing user's code. I
am not an implementor, so do not have a vested interest in
the names we chose, e.g., I am very willing to change the
names.
One way to solve pollution is to put all the new macros
and functions into <stdlia.h> rather than in the logical place
that they belong (<limits.h>, <float.h>, <math.h>, <stdlib.h>,
<tgmath.h>). That should allow existing code to
continue to compile with a C9X compiler without change.
A first pass at the proposal had them in the logical place
(and that drew some complaints).
The current proposal has them all in <stdlia.h> as name
space pollution is a big concern with some members (do not
break existing code just because it happens to be compiled
with a C9X compiler).
--
What is the name of the new header? <lia.h> or <stdlia.h>?
<stdlia.h>
* Environment
OPEN Should integer environment/flags be part of (or separate
from) fp env./flags? I believe that the floating-point
exception flags and the integer exception flags may have
different status associated with the flags, hence, fexcept_t
may not be able to hold the integer related information. On
the other hand, fexcept_t could have two sub-structures, one
for integer and another for floating-point. Same for fenv_t.
Choices are:
Combined arithmetic (floating-point and integer)
environment, types, macros, and functions. Basically,
change 'floating-point' to 'arithmetic' in 7.6 <fenv.h>
and add FE_INT_INVALID, FE_INT_OVERFLOW, FE_INT_DIVBYZERO.
Could also rename header to <stdmath.h>.
Separate floating-point and integer, minimal.
Add INT_INVALID, INT_OVERFLOW, and INT_DIVBYZERO macros.
Duplicate fe(clear/raise/test)except functions and
change 'fe' to 'ie' and floating-point to integer.
This is the current proposal (based upon very little
feedback). I believe that this is the wrong approach.
Separate floating-point and integer, maximal.
Duplicate almost the entire <fenv.h> into <stdlia.h>,
and change floating-point to integer throughout. Would
drop rounding macros/functions. Replace the five FE_*
exception macros with INT_INVALID, INT_OVERFLOW, and
INT_DIVBYZERO. This is a lot of work for (assumed)
very little usage.
I believe that the best approach is to add the integer
environment to the existing floating-point environment, call
it the arithmetic environment and rename the header to
<stdmath.h>. Also, change 'FENV' to 'LIAENV', 'fenv' to
'liaenv', 'FE' to 'LIA' and 'fe' to 'lia' throughout the
header (and rest of standard). This allows one interface to
the math environment, and allows existing implementations to
continue to use their <fenv.h>, which is no longer part of
C9X. <stdmath.h> would replace <fenv.h> in C9X.
We could leave the (floating-point) rounding macros and
functions as is, since there is no extension for integer.
If someone wants to only support IEC 559, then they can
leave FE_INT_INVALID, FE_INT_OVERFLOW, FE_INT_DIVBYZERO
undefined in <stdmath.h> and it should act just like the
existing <fenv.h>
--
It is assumed that the integer and floating-point
environments each consist of a control word and a status
word. The status word contains bits (sticky flags) to
indicate the state of past operations. The status word and
control word need not be hardware registers, they may be in
memory and maintained by system software.
The floating-point environment consists of:
status
sticky flags
invalid
div-by-zero
overflow
underflow
inexact
optional
current operation being performed
exception(s) of current operation
exception(s) still pending
operand values
destination's precision
rounded result
control
rounding
precision (optional)
trap enable/disable (optional)
invalid
div-by-zero
overflow
underflow
inexact
The integer environment consists of:
status
sticky flags
invalid
div-by-zero
overflow
optional
current operation being performed
exception of current operation
exception(s) still pending
operand values
destination's precision
wrapped result
control
trap enable/disable (optional)
invalid
div-by-zero
overflow
The integer trap enable/disable control bits also indicate
if notification is via a trap handler (enabled) or via
setting a flag and returning a continuation result (disabled).
* Pragmas
What is scope of LIA_WRAP pragma? Statement, compound
statement, function, or translation unit? Same as
FENV_ACCESS pragma.
--
OPEN What is the meaning of #pragma STDC LIA_NOTIFY opt before
<stdlia.h> has been included? Must <stdlia.h> be included to give
it meaning? Should the include be removed from the synopsis?
I believe that all the pragmas should be independent of
headers and should not have the include of a header in the
synopsis section.
Until that question is resolved, treat lack of include
<stdlia.h> as if #pragma STDC LIA_NOTIFY UNDEF.
--
Should there be a IENV_ACCESS pragma to allow the user to
inform the implementation that the program might access the
integer environment? I believe not, since, if the user does
#pragma STDC LIA_NOTIFY FLAGS, they have told the
implementation that the flags will be tested at program
termination (at the least).
--
Is there interaction between FENV_ACCESS and LIA_NOTIFY
pragmas? Does one imply the other? LIA_NOTIFY FLAGS |
TRAP | DYNAMIC implies FENV_ACCESS ON
--
What happens if one translation unit contains
#pragma STDC LIA_NOTIFY IGNORE
and another translation unit contains
#pragma STDC LIA_NOTIFY TRAP
and these two translation units are part of the same
program?
It is implementation defined.
--
OPEN What is the scope of LIA_NOTIFY pragma? Program,
translation unit, function, compound statement? LIA-1 only
requires that the entire program be translated under control
of the same notification method (FLAGS or TRAP). As this
proposal is currently written, it is compound statement.
--
Should it be implementation defined how IGNORE suppresses
the final output? It could be done at link time.
* Boolean
OPEN What about <stdbool.h> for booleans?
As I understand N738 and what we did in London this past
June, bool, true, and false are keywords if and only if
<stdbool.h> is included. That is, <stdbool.h> is a
conditionaly normative header.
If we add bool here, should it be added in the rest of the
standard? For example, the is* macros?
Therefore, I believe that <stdlia.h> cannot depend upon bool,
true, and false. I am willing to change that belief if the
committee directs me to.
* Characteristics Macros
Should rounding error be constant or track changes to
rounding mode? I believe that it should be like FLT_ROUNDS
(a function) and track the runtime changes.
--
OPEN What value should FLT_RND_ERR have? For C9X conformance, it
should be the smallest value that allows most (all?) current
implementations to conform. For LIA-1 conformance, it must
be 1.0. The LIA-1 document says that Cray cannot meet 1.0,
but does not say which machines, nor what error they can
meet.
I have been told that for most older Cray computers, the
errors for the operations are as follows:
+ 1 ulp
- 2 ulp
* 2 ulp
/ 7 ulp
So, should there be a separate symbol for the error for each
operation? LIA-1 wants the same error bound and rounding
function for all of +, -, *, and /, so one symbol works for
LIA, but not for general C9X. In looking at LIA-2 (which
address the math library), they want a parameter (such as
max_err_OP, where OP is sin, log, ...) that documents the
maximum error for each math library operation. In looking
at WG14 document N756 which discusses the liagetstatus()
function, it appears that we should have a list of symbols
(either macros or an enum) for each floating-point operation
and math library function and another function lia_error()
that would take an operation/math library function name
symbol and return the maximum error in ulps for that
operation. This idea has not been fleshed out (waiting to
see were we are going with LIA).
--
OPEN Should there be three *_RND_ERR macros (one for each type),
or is one OK for all the floating point types? Might need
three if float and double are IEC 559 and long double is
not, hence, long double has more error. It would be better
if we only had one (just like one FLT_ROUNDS). Current
proposal uses just one (until someone says we need three).
--
The definition of modulo (INT_OUT_OF_BOUNDS) has been extended
to include undefined to cover current C behavior: undefined
vs wrapped vs notification.
--
Should there be a distinct denotation for modulo for each
integral type (int, long, long long)? There is no LIA-1
requirement that each signed integral type have its own
(changeable) characteristics. If we go for that additional
control by the implementor, then the optional LIA_WRAP
pragma needs to be extended to each integral type. For now,
I have removed LONG_OUT_OF_BOUNDS and LLONG_OUT_OF_BOUNDS.
* Functions
** Exponent and mantissa / fraction / significand
The model representation of LIA and C9X floating-point
numbers is: sign * p-digit fraction * base ** exponent,
where fraction is in the range [ 1/FLT_RADIX ... 1.0 ).
The model representation has nothing to do with the hardware
representation.
LIA has functions to get the exponent and the fraction from
the model representation. LIA also has functions to get the
integer part and the fraction part of a value.
So, for the value 3.1416 in base 10, the representation is
+ 0.31416 * 10 ** 1.
1.0 = exponent of model representation
0.31416 = fraction of model representation
3.0 = integer part of value
0.1416 = fraction part of value
So, the 'fraction' of x could mean the fraction of the value
or the fraction of the model representation. Fraction of
the model representation has other similar names: mantissa
or significand. From these names, the name of the function
could be derived. LIA recommends 'fract' for the fraction
of the model representation and 'frcprt' for the fraction
part of the value. I chose 'fracrep' and 'fracval'. Are
there better choices (such as mantissa)?
For non-zero finite numbers, 1/radix <= fracrep(|x|) < 1.
--
Should there be a function for exponent of the model
representation ('exprep'), or, should it just be
documentation that it is logb(x)+1? Documentation.
** Integer and fraction
Should there be a function for integer part of value
('intval'), or, should it just be documentation that it can
be obtained via modf()? Documentation.
--
Should there be a function for fraction part of value
('fracval'), or, should it just be documentation that it can
be obtained via modf()? Documentation.
** pred and succ
Should fpred(-INFINITY) return -INFINITY or raise invalid
and return a NaN? -INFINITY.
Should fpred(0.0) raise underflow? No.
fpred(subnormal) does not raise underflow (unlike
nextafter). But, then, LIA-2 does not raise underflow for
any math function that takes a subnormal and returns a
subnormal, eg, sin(subnorm) is same subnorm w/o underflow.
Their reason is underflow was already raised when the
subnormal was created. That reason is faulty for
sin(DBL_TRUE_MIN) since it is an exact input value (not the
result of previous arithmetic). Also, the mathematical
value of sin(subnorm) is not exactly the same subnorm, so
the value returned is an inexact approximation of the true
result.
fpred and fsucc(subnorm) take an exact value and return an
exact value. There is no denormalization loss nor inexact,
so underflow should not be raised. That same statement
should apply to nextafter (but does not, why?).
--
Should fsucc(+INFINITY) return +INFINITY or raise invalid
and return a NaN? +INFINITY.
Should fsucc(0.0) raise underflow? No.
fsucc(subnormal) does not raise underflow (unlike
nextafter).
** FP -> int conversions
Should the floating to integral conversion [*cvt()]
operators be macros (similar to isless())? Or, is it better
to have them be type-generic functions? Should they be in
<stdlia.h> or <math.h>? It is easier wording-wise to put them
in <math.h> if we want type-generic functions. Macros.
--
Should icvt(-0.0) return -0 (if it can be represented), else
+0? The same question applies to lcvt and llcvt. Not -0
--
The casts from floating to integral cannot be used (to meet
LIA-1 requirements) as they truncate (round toward zero) and
LIA-1 requires round to nearest. LIA-1 also requires, if
modulo = wrap, that integer overflow not be raised. The
lround() and lrint() family of functions cannot be used as
they may raise FE_INVALID.
On the other hand, combining round() or rint() with a cast,
(integer cast)round(floating), meets LIA for the round to
nearest requirement.
It is assumed that there is a global integer control mode to
indicate if signed integer types are wrapping or not. That
control mode will affect the behavior of the *cvt()
functions.
--
Should icvt(INFINITY) raise INT_INVALID or FE_INVALID? The
same question applies to all the *cvt macros. Since it is
the integers that cannot represent infinity, it seems like
it should be INT_INVALID, but some hardware does FE_INVALID.
Implementation defined via FP2INT_OF_INF.
--
OPEN FPCE: Should lround, lrint use FP2INT_OF_INF/NAN/LARGE?
** Sign ( FP and int )
OPEN Should *sgn() be replaced with int sgn( maxint_t i);? That
would replace 3 functions with 1. Maybe. How is maxint_t
defined (include <inttypes.h> or redefine it here)? In
general, LIA-1 wants each function to return the same type
as the type of the argument. For the sign, where the return
values are -1, 0, and +1, the type would not matter. It
could perhaps be replaced by a macro like isnan().
Same idea: fsgn*() be replaced with float fsgn(long double);
* Optional features
Who determines if signed integers wrap or overflow: C9X,
implementation, or user?
LIA-1, in section A.5.1.0.2, says: Since modulo integers are
dangerous, programmers should always have the option of
using non-modulo (overflow checking) integers instead.
The optional LIA_WRAP pragma allows the user to choose.
INT_OUT_OF_BOUNDS is how the program can find the choice.
OPEN Should this pragma be made normative?
--
Should the addition of NANS* macros be moved from proposed
Annex I to 7.7 <math.h> and Annex F (IEC 559)? Signaling
NaNs are a required part of IEC 559, so to claim conformance
to C and IEC 559, the language and the implementation need
Signaling NaNs. This is not a LIA-1 issue, but an IEC 559
issue.
One standard can override another standard. So, until
someone has a good use of SNaNs, forget about them.
* Deviations
This binding of LIA-1 to C deviates from LIA-1 in the
following ways:
1) Signaling NaNs on IEC 559 implementations are not
required (instead of being required).
2) Pole is used for finite non-zero / zero (instead of
Undefined). DISTINGUISH_INT_DIV_BY_ZERO and
DISTINGUISH_FP_DIV_BY_ZERO indicate if zero/zero can be
distinguished from finite non-zero / zero.
3) Conversion from floating-point type to integral type for
values out-of-bounds shall raise one of INT_OVERFLOW or
FE_INVALID (instead of just INT_OVERFLOW). FP2INT_OF_LARGE
indicates which one.
* The end