integer and div clamped a float outside the integer rangeWhat happened. integer applied to a float whose integral part is larger than any CLIPS integer returned the largest CLIPS integer. div did the same to each of its arguments before dividing.
(printout t "integer-1e20: " (integer 1e20) crlf)
(printout t "div-1e20: " (div 1e20 1) crlf)
printed
integer-1e20: 9223372036854775807
div-1e20: 9223372036854775807
(div 1 1e20) printed 0, which is 1 divided by 9223372036854775807 rather than by the argument written. An infinity came back as 9223372036854775807 and a NaN as 0.
Why it is a fix and not a preference. Section 12.5.10 of the Basic Programming Guide says integer "converts its only argument ... to type integer and returns this value". 9223372036854775807 is not 1e20. Section 12.5.5 says of div that "each argument is automatically converted to an integer and integer division is performed"; 1e20 has no such conversion, and what the division used was a different number from the one in the call.
The same directory already treats one non-representable integer result as an evaluation error: DivFunction in bmathfun.c on LLONG_MIN / -1, ModFunction in emathfun.c on the same pair, both reporting through ArgumentOverUnderflowErrorMessage.
Where it came from. CVCoerceToInteger in evaluatn.h is a macro that casts:
#define CVCoerceToInteger(cv) (((cv)->header->type == INTEGER_TYPE) ? \
((cv)->integerValue->contents) : \
((long long) (cv)->floatValue->contents))
IntegerFunction reached that cast for a float argument and DivFunction reached it for every argument. A float-to-integer conversion in C has no result when the integral part does not fit the destination, so what came back was whatever the host produced — on this host the ends of the range, and 0 for a NaN.
The change. FloatFitsInInteger in evaluatn.c, declared in evaluatn.h beside the macro it guards, reports whether the integral part of a double is a value long long can represent:
return (theFloat >= -9223372036854775808.0) &&
(theFloat < 9223372036854775808.0);
Both bounds are exact. 2⁶³ is the smallest double above LLONG_MAX, so every double below it truncates to LLONG_MAX or less. −2⁶³ is LLONG_MIN itself and the next double below it is −2⁶³ − 2048, so the lower comparison needs no slack. A NaN fails both comparisons: it has no integral part to convert.
IntegerFunction asks before its cast, and DivFunction asks for its first argument and for each later one. On a float that does not fit, each reports through ArgumentOverUnderflowErrorMessage, sets the evaluation error, and returns without casting.
What was checked. Eight calls that must report: (integer 1e20) and its negative, 2⁶³ exactly, which is one step past the largest long long; both infinities, (div 1e20 1), (div 1 1e20), and (div 1 2 -1e20), which puts the out-of-range argument third. All eight report, naming integer five times and div three times, and none prints a value. A NaN built as an infinity divided by itself reaches integer as a float, reports, and prints nothing. A call after the reports still evaluates.
Then the conversions that must still happen: the examples the guide gives for both functions; truncation toward zero from above and from below; 9223372036854774784, the largest double inside the range, which converts to itself; −9223372036854775808 as a double, which is exact and converts; and a float as div's first and as a later argument. None of them reports.
The reason to care about this one: 9223372036854775807 reads as a number someone meant, so a clamped conversion propagates as a plausible value rather than as a failure.
Anonymous
Present in 7.0? Not checked.