* 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: tydeman@tybor.com 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: to , 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: , 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, code sample 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. 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 */ } }; -- 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 or ? Assume . -- 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 rather than in the logical place that they belong (, , , , ). 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 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? or ? * 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 and add FE_INT_INVALID, FE_INT_OVERFLOW, FE_INT_DIVBYZERO. Could also rename header to . 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 into , 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 . 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 , which is no longer part of C9X. would replace 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 and it should act just like the existing -- 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 has been included? Must 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 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 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 is included. That is, 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 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 or ? It is easier wording-wise to put them in 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 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 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