Index: doc/expr.n ================================================================== --- doc/expr.n +++ doc/expr.n @@ -449,12 +449,12 @@ string(n), Tcl(n), while(n) .SH KEYWORDS arithmetic, boolean, compare, expression, fuzzy comparison .SH COPYRIGHT .nf -Copyright (c) 1993 The Regents of the University of California. -Copyright (c) 1994-2000 Sun Microsystems Incorporated. -Copyright (c) 2005 by Kevin B. Kenny . All rights reserved. +Copyright \(co 1993 The Regents of the University of California. +Copyright \(co 1994-2000 Sun Microsystems Incorporated. +Copyright \(co 2005 by Kevin B. Kenny . All rights reserved. .fi '\" Local Variables: '\" mode: nroff '\" End: ADDED doc/fpclassify.n Index: doc/fpclassify.n ================================================================== --- /dev/null +++ doc/fpclassify.n @@ -0,0 +1,83 @@ +'\" +'\" Copyright (c) 2018 by Kevin B. Kenny . All rights reserved +'\" Copyright (c) 2019 by Donal Fellows +'\" +'\" See the file "license.terms" for information on usage and redistribution +'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. +'\" +.TH fpclassify n 8.7 Tcl "Tcl Float Classifier" +.so man.macros +.BS +'\" Note: do not modify the .SH NAME line immediately below! +.SH NAME +fpclassify \- Floating point number classification of Tcl values +.SH SYNOPSIS +package require \fBTcl 8.7\fR +.sp +\fBfpclassify \fIvalue\fR +.BE +.SH DESCRIPTION +The \fBfpclassify\fR command takes a floating point number, \fIvalue\fR, and +returns one of the following strings that describe it: +.TP +\fBzero\fR +. +\fIvalue\fR is a floating point zero. +.TP +\fBsubnormal\fR +. +\fIvalue\fR is the result of a gradual underflow. +.TP +\fBnormal\fR +. +\fIvalue\fR is an ordinary floating-point number (not zero, subnormal, +infinite, nor NaN). +.TP +\fBinfinite\fR +. +\fIvalue\fR is a floating-point infinity. +.TP +\fBnan\fR +. +\fIvalue\fR is Not-a-Number. +.PP +The \fBfpclassify\fR command throws an error if value is not a floating-point +value and cannot be converted to one. +.SH EXAMPLE +.PP +This shows how to check whether the result of a computation is numerically +safe or not. (Note however that it does not guard against numerical errors; +just against representational problems.) +.PP +.CS +set value [command-that-computes-a-value] +switch [\fBfpclassify\fR $value] { + normal - zero { + puts "Result is $value" + } + infinite { + puts "Result is infinite" + } + subnormal { + puts "Result is $value - WARNING! precision lost" + } + nan { + puts "Computation completely failed" + } +} +.CE +.SH "SEE ALSO" +expr(n), mathfunc(n) +.SH KEYWORDS +floating point +.SH STANDARDS +This command depends on the \fBfpclassify\fR() C macro conforming to +.QW "ISO C99" +(i.e., to ISO/IEC 9899:1999). +.SH COPYRIGHT +.nf +Copyright \(co 2018 by Kevin B. Kenny . All rights reserved +.fi +'\" Local Variables: +'\" mode: nroff +'\" End: Index: doc/mathfunc.n ================================================================== --- doc/mathfunc.n +++ doc/mathfunc.n @@ -44,12 +44,28 @@ \fB::tcl::mathfunc::fmod\fR \fIx\fR \fIy\fR .br \fB::tcl::mathfunc::hypot\fR \fIx\fR \fIy\fR .br \fB::tcl::mathfunc::int\fR \fIarg\fR +.br +.VS "8.7, TIP 521" +\fB::tcl::mathfunc::isfinite\fR \fIarg\fR +.br +\fB::tcl::mathfunc::isinf\fR \fIarg\fR +.br +\fB::tcl::mathfunc::isnan\fR \fIarg\fR +.br +\fB::tcl::mathfunc::isnormal\fR \fIarg\fR +.VE "8.7, TIP 521" .br \fB::tcl::mathfunc::isqrt\fR \fIarg\fR +.br +.VS "8.7, TIP 521" +\fB::tcl::mathfunc::issubnormal\fR \fIarg\fR +.br +\fB::tcl::mathfunc::isunordered\fR \fIx y\fR +.VE "8.7, TIP 521" .br \fB::tcl::mathfunc::log\fR \fIarg\fR .br \fB::tcl::mathfunc::log10\fR \fIarg\fR .br @@ -90,19 +106,21 @@ directly. .PP Tcl supports the following mathematical functions in expressions, all of which work solely with floating-point numbers unless otherwise noted: .DS -.ta 3c 6c 9c +.ta 3.2c 6.4c 9.6c \fBabs\fR \fBacos\fR \fBasin\fR \fBatan\fR \fBatan2\fR \fBbool\fR \fBceil\fR \fBcos\fR \fBcosh\fR \fBdouble\fR \fBentier\fR \fBexp\fR \fBfloor\fR \fBfmod\fR \fBhypot\fR \fBint\fR -\fBisqrt\fR \fBlog\fR \fBlog10\fR \fBmax\fR -\fBmin\fR \fBpow\fR \fBrand\fR \fBround\fR -\fBsin\fR \fBsinh\fR \fBsqrt\fR \fBsrand\fR -\fBtan\fR \fBtanh\fR \fBwide\fR +\fBisfinite\fR \fBisinf\fR \fBisnan\fR \fBisnormal\fR +\fBisqrt\fR \fBissubnormal\fR \fBisunordered\fR \fBlog\fR +\fBlog10\fR \fBmax\fR \fBmin\fR \fBpow\fR +\fBrand\fR \fBround\fR \fBsin\fR \fBsinh\fR +\fBsqrt\fR \fBsrand\fR \fBtan\fR \fBtanh\fR +\fBwide\fR .DE .PP In addition to these predefined functions, applications may define additional functions by using \fBproc\fR (or any other method, such as \fBinterp alias\fR or \fBTcl_CreateObjCommand\fR) to define @@ -207,16 +225,61 @@ is determined, and then the low order bits of that integer value up to the machine word size are returned as an integer value. For reference, the number of bytes in the machine word are stored in the \fBwordSize\fR element of the \fBtcl_platform\fR array. .TP +\fBisfinite \fIarg\fR +.VS "8.7, TIP 521" +Returns 1 if the floating-point number \fIarg\fR is finite. That is, if it is +zero, subnormal, or normal. Returns 0 if the number is infinite or NaN. Throws +an error if \fIarg\fR cannot be promoted to a floating-point value. +.VE "8.7, TIP 521" +.TP +\fBisinf \fIarg\fR +.VS "8.7, TIP 521" +Returns 1 if the floating-point number \fIarg\fR is infinite. Returns 0 if the +number is finite or NaN. Throws an error if \fIarg\fR cannot be promoted to a +floating-point value. +.VE "8.7, TIP 521" +.TP +\fBisnan \fIarg\fR +.VS "8.7, TIP 521" +Returns 1 if the floating-point number \fIarg\fR is Not-a-Number. Returns 0 if +the number is finite or infinite. Throws an error if \fIarg\fR cannot be +promoted to a floating-point value. +.VE "8.7, TIP 521" +.TP +\fBisnormal \fIarg\fR +.VS "8.7, TIP 521" +Returns 1 if the floating-point number \fIarg\fR is normal. Returns 0 if the +number is zero, subnormal, infinite or NaN. Throws an error if \fIarg\fR +cannot be promoted to a floating-point value. +.VE "8.7, TIP 521" +.TP \fBisqrt \fIarg\fR . Computes the integer part of the square root of \fIarg\fR. \fIArg\fR must be a positive value, either an integer or a floating point number. Unlike \fBsqrt\fR, which is limited to the precision of a floating point number, \fIisqrt\fR will return a result of arbitrary precision. +.TP +\fBissubnormal \fIarg\fR +.VS "8.7, TIP 521" +Returns 1 if the floating-point number \fIarg\fR is subnormal, i.e., the +result of gradual underflow. Returns 0 if the number is zero, normal, infinite +or NaN. Throws an error if \fIarg\fR cannot be promoted to a floating-point +value. +.VE "8.7, TIP 521" +.TP +\fBisunordered \fIx y\fR +.VS "8.7, TIP 521" +Returns 1 if \fIx\fR and \fIy\fR cannot be compared for ordering, that is, if +either one is NaN. Returns 0 if both values can be ordered, that is, if they +are both chosen from among the set of zero, subnormal, normal and infinite +values. Throws an error if either \fIx\fR or \fIy\fR cannot be promoted to a +floating-point value. +.VE "8.7, TIP 521" .TP \fBlog \fIarg\fR . Returns the natural logarithm of \fIarg\fR. \fIArg\fR must be a positive value. @@ -290,16 +353,16 @@ . The argument may be any numeric value. The integer part of \fIarg\fR is determined, and then the low order 64 bits of that integer value are returned as an integer value. .SH "SEE ALSO" -expr(n), mathop(n), namespace(n) +expr(n), fpclassify(n), mathop(n), namespace(n) .SH "COPYRIGHT" .nf -Copyright (c) 1993 The Regents of the University of California. -Copyright (c) 1994-2000 Sun Microsystems Incorporated. -Copyright (c) 2005, 2006 by Kevin B. Kenny . +Copyright \(co 1993 The Regents of the University of California. +Copyright \(co 1994-2000 Sun Microsystems Incorporated. +Copyright \(co 2005, 2006 by Kevin B. Kenny . .fi '\" Local Variables: '\" mode: nroff '\" fill-column: 78 '\" End: Index: generic/tclBasic.c ================================================================== --- generic/tclBasic.c +++ generic/tclBasic.c @@ -21,10 +21,13 @@ #include "tclOOInt.h" #include "tclCompile.h" #include "tommath.h" #include #include +#ifndef fpclassify /* Older MSVC */ +#include +#endif /* !fpclassify */ #define INTERP_STACK_INITIAL_SIZE 2000 #define CORO_STACK_INITIAL_SIZE 200 /* @@ -127,18 +130,25 @@ static Tcl_ObjCmdProc ExprCeilFunc; static Tcl_ObjCmdProc ExprDoubleFunc; static Tcl_ObjCmdProc ExprFloorFunc; static Tcl_ObjCmdProc ExprIntFunc; static Tcl_ObjCmdProc ExprIsqrtFunc; +static Tcl_ObjCmdProc ExprIsFiniteFunc; +static Tcl_ObjCmdProc ExprIsInfinityFunc; +static Tcl_ObjCmdProc ExprIsNaNFunc; +static Tcl_ObjCmdProc ExprIsNormalFunc; +static Tcl_ObjCmdProc ExprIsSubnormalFunc; +static Tcl_ObjCmdProc ExprIsUnorderedFunc; static Tcl_ObjCmdProc ExprMaxFunc; static Tcl_ObjCmdProc ExprMinFunc; static Tcl_ObjCmdProc ExprRandFunc; static Tcl_ObjCmdProc ExprRoundFunc; static Tcl_ObjCmdProc ExprSqrtFunc; static Tcl_ObjCmdProc ExprSrandFunc; static Tcl_ObjCmdProc ExprUnaryFunc; static Tcl_ObjCmdProc ExprWideFunc; +static Tcl_ObjCmdProc FloatClassifyObjCmd; static void MathFuncWrongNumArgs(Tcl_Interp *interp, int expected, int actual, Tcl_Obj *const *objv); static Tcl_NRPostProc NRCoroutineCallerCallback; static Tcl_NRPostProc NRCoroutineExitCallback; static Tcl_NRPostProc NRCommand; @@ -254,10 +264,11 @@ {"eval", Tcl_EvalObjCmd, NULL, TclNREvalObjCmd, CMD_IS_SAFE}, {"expr", Tcl_ExprObjCmd, TclCompileExprCmd, TclNRExprObjCmd, CMD_IS_SAFE}, {"for", Tcl_ForObjCmd, TclCompileForCmd, TclNRForObjCmd, CMD_IS_SAFE}, {"foreach", Tcl_ForeachObjCmd, TclCompileForeachCmd, TclNRForeachCmd, CMD_IS_SAFE}, {"format", Tcl_FormatObjCmd, TclCompileFormatCmd, NULL, CMD_IS_SAFE}, + {"fpclassify", FloatClassifyObjCmd, NULL, NULL, CMD_IS_SAFE}, {"global", Tcl_GlobalObjCmd, TclCompileGlobalCmd, NULL, CMD_IS_SAFE}, {"if", Tcl_IfObjCmd, TclCompileIfCmd, TclNRIfObjCmd, CMD_IS_SAFE}, {"incr", Tcl_IncrObjCmd, TclCompileIncrCmd, NULL, CMD_IS_SAFE}, {"join", Tcl_JoinObjCmd, NULL, NULL, CMD_IS_SAFE}, {"lappend", Tcl_LappendObjCmd, TclCompileLappendCmd, NULL, CMD_IS_SAFE}, @@ -422,11 +433,17 @@ { "exp", ExprUnaryFunc, (ClientData) exp }, { "floor", ExprFloorFunc, NULL }, { "fmod", ExprBinaryFunc, (ClientData) fmod }, { "hypot", ExprBinaryFunc, (ClientData) hypot }, { "int", ExprIntFunc, NULL }, + { "isfinite", ExprIsFiniteFunc, NULL }, + { "isinf", ExprIsInfinityFunc, NULL }, + { "isnan", ExprIsNaNFunc, NULL }, + { "isnormal", ExprIsNormalFunc, NULL }, { "isqrt", ExprIsqrtFunc, NULL }, + { "issubnormal", ExprIsSubnormalFunc, NULL, }, + { "isunordered", ExprIsUnorderedFunc, NULL, }, { "log", ExprUnaryFunc, (ClientData) log }, { "log10", ExprUnaryFunc, (ClientData) log10 }, { "max", ExprMaxFunc, NULL }, { "min", ExprMinFunc, NULL }, { "pow", ExprBinaryFunc, (ClientData) pow }, @@ -8277,10 +8294,319 @@ * will always succeed. */ return ExprRandFunc(clientData, interp, 1, objv); } + +/* + *---------------------------------------------------------------------- + * + * Double Classification Functions -- + * + * This page contains the functions that implement all of the built-in + * math functions for classifying IEEE doubles. + * + * These have to be a little bit careful while Tcl_GetDoubleFromObj() + * rejects NaN values, which these functions *explicitly* accept. + * + * Results: + * Each function returns TCL_OK if it succeeds and pushes an Tcl object + * holding the result. If it fails it returns TCL_ERROR and leaves an + * error message in the interpreter's result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +/* + * Older MSVC is supported by Tcl, but doesn't have fpclassify(). Of course. + * But it does have _fpclass() which does almost the same job. + * + * This makes it conform to the C99 standard API, and just delegates to the + * standard macro on platforms that do it correctly. + */ + +static inline int +ClassifyDouble( + double d) +{ +#ifdef fpclassify + return fpclassify(d); +#else /* !fpclassify */ +#define FP_ZERO 0 +#define FP_NORMAL 1 +#define FP_SUBNORMAL 2 +#define FP_INFINITE 3 +#define FP_NAN 4 + + switch (_fpclass(d)) { + case _FPCLASS_NZ: + case _FPCLASS_PZ: + return FP_ZERO; + case _FPCLASS_NN: + case _FPCLASS_PN: + return FP_NORMAL; + case _FPCLASS_ND: + case _FPCLASS_PD: + return FP_SUBNORMAL; + case _FPCLASS_NINF: + case _FPCLASS_PINF: + return FP_INFINITE; + default: + Tcl_Panic("result of _fpclass() outside documented range!"); + case _FPCLASS_QNAN: + case _FPCLASS_SNAN: + return FP_NAN; + } +#endif /* fpclassify */ +} + +static int +ExprIsFiniteFunc( + ClientData ignored, + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count */ + Tcl_Obj *const *objv) /* Actual parameter list */ +{ + double d; + ClientData ptr; + int type, result = 0; + + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + + if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { + return TCL_ERROR; + } + if (type != TCL_NUMBER_NAN) { + if (Tcl_GetDoubleFromObj(interp, objv[1], &d) != TCL_OK) { + return TCL_ERROR; + } + type = ClassifyDouble(d); + result = (type != FP_INFINITE && type != FP_NAN); + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); + return TCL_OK; +} + +static int +ExprIsInfinityFunc( + ClientData ignored, + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count */ + Tcl_Obj *const *objv) /* Actual parameter list */ +{ + double d; + ClientData ptr; + int type, result = 0; + + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + + if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { + return TCL_ERROR; + } + if (type != TCL_NUMBER_NAN) { + if (Tcl_GetDoubleFromObj(interp, objv[1], &d) != TCL_OK) { + return TCL_ERROR; + } + result = (ClassifyDouble(d) == FP_INFINITE); + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); + return TCL_OK; +} + +static int +ExprIsNaNFunc( + ClientData ignored, + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count */ + Tcl_Obj *const *objv) /* Actual parameter list */ +{ + double d; + ClientData ptr; + int type, result = 1; + + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + + if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { + return TCL_ERROR; + } + if (type != TCL_NUMBER_NAN) { + if (Tcl_GetDoubleFromObj(interp, objv[1], &d) != TCL_OK) { + return TCL_ERROR; + } + result = (ClassifyDouble(d) == FP_NAN); + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); + return TCL_OK; +} + +static int +ExprIsNormalFunc( + ClientData ignored, + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count */ + Tcl_Obj *const *objv) /* Actual parameter list */ +{ + double d; + ClientData ptr; + int type, result = 0; + + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + + if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { + return TCL_ERROR; + } + if (type != TCL_NUMBER_NAN) { + if (Tcl_GetDoubleFromObj(interp, objv[1], &d) != TCL_OK) { + return TCL_ERROR; + } + result = (ClassifyDouble(d) == FP_NORMAL); + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); + return TCL_OK; +} + +static int +ExprIsSubnormalFunc( + ClientData ignored, + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count */ + Tcl_Obj *const *objv) /* Actual parameter list */ +{ + double d; + ClientData ptr; + int type, result = 0; + + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + + if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { + return TCL_ERROR; + } + if (type != TCL_NUMBER_NAN) { + if (Tcl_GetDoubleFromObj(interp, objv[1], &d) != TCL_OK) { + return TCL_ERROR; + } + result = (ClassifyDouble(d) == FP_SUBNORMAL); + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); + return TCL_OK; +} + +static int +ExprIsUnorderedFunc( + ClientData ignored, + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count */ + Tcl_Obj *const *objv) /* Actual parameter list */ +{ + double d; + ClientData ptr; + int type, result = 0; + + if (objc != 3) { + MathFuncWrongNumArgs(interp, 3, objc, objv); + return TCL_ERROR; + } + + if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { + return TCL_ERROR; + } + if (type == TCL_NUMBER_NAN) { + result = 1; + } else { + d = *((const double *) ptr); + result = (ClassifyDouble(d) == FP_NAN); + } + + if (TclGetNumberFromObj(interp, objv[2], &ptr, &type) != TCL_OK) { + return TCL_ERROR; + } + if (type == TCL_NUMBER_NAN) { + result |= 1; + } else { + d = *((const double *) ptr); + result |= (ClassifyDouble(d) == FP_NAN); + } + + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); + return TCL_OK; +} + +static int +FloatClassifyObjCmd( + ClientData ignored, + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count */ + Tcl_Obj *const *objv) /* Actual parameter list */ +{ + double d; + Tcl_Obj *objPtr; + ClientData ptr; + int type; + + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "floatValue"); + return TCL_ERROR; + } + + if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { + return TCL_ERROR; + } + if (type == TCL_NUMBER_NAN) { + goto gotNaN; + } else if (Tcl_GetDoubleFromObj(interp, objv[1], &d) != TCL_OK) { + return TCL_ERROR; + } + switch (ClassifyDouble(d)) { + case FP_INFINITE: + TclNewLiteralStringObj(objPtr, "infinite"); + break; + case FP_NAN: + gotNaN: + TclNewLiteralStringObj(objPtr, "nan"); + break; + case FP_NORMAL: + TclNewLiteralStringObj(objPtr, "normal"); + break; + case FP_SUBNORMAL: + TclNewLiteralStringObj(objPtr, "subnormal"); + break; + case FP_ZERO: + TclNewLiteralStringObj(objPtr, "zero"); + break; + default: + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "unable to classify number: %f", d)); + return TCL_ERROR; + } + Tcl_SetObjResult(interp, objPtr); + return TCL_OK; +} /* *---------------------------------------------------------------------- * * MathFuncWrongNumArgs -- Index: tests/expr.test ================================================================== --- tests/expr.test +++ tests/expr.test @@ -131,11 +131,11 @@ 12days 1 1 1 set result [string length $xxx] unset xxx return $result } - + # start of tests catch {unset a b i x} test expr-1.1 {TclCompileExprCmd: no expression} { @@ -7160,19 +7160,152 @@ list [expr {$a eq {}}] [expr {$a < {}}] [expr {$a > {}}] [ string match {*no string representation*} [ ::tcl::unsupported::representation $a]] } {0 0 1 1} - - -# cleanup -if {[info exists a]} { - unset a -} -catch {unset min} -catch {unset max} +foreach func {isfinite isinf isnan isnormal issubnormal} { + test expr-53.1.$func {float classification: basic arg handling} -body { + expr ${func}() + } -returnCodes error -result "too few arguments for math function \"$func\"" + test expr-53.2.$func {float classification: basic arg handling} -body { + expr ${func}(1,2) + } -returnCodes error -result "too many arguments for math function \"$func\"" + test expr-53.3.$func {float classification: basic arg handling} -body { + expr ${func}(true) + } -returnCodes error -result {expected number but got "true"} + test expr-53.4.$func {float classification: basic arg handling} -body { + expr ${func}("gorp") + } -returnCodes error -result {expected number but got "gorp"} + test expr-53.5.$func {float classification: basic arg handling} -body { + expr ${func}(1.0) + } -match glob -result * + test expr-53.6.$func {float classification: basic arg handling} -body { + expr ${func}(0x123) + } -match glob -result * +} + +test expr-54.0 {float classification: isfinite} {expr {isfinite(1.0)}} 1 +test expr-54.1 {float classification: isfinite} {expr {isfinite(-1.0)}} 1 +test expr-54.2 {float classification: isfinite} {expr {isfinite(0.0)}} 1 +test expr-54.3 {float classification: isfinite} {expr {isfinite(-0.0)}} 1 +test expr-54.4 {float classification: isfinite} {expr {isfinite(1/Inf)}} 1 +test expr-54.5 {float classification: isfinite} {expr {isfinite(-1/Inf)}} 1 +test expr-54.6 {float classification: isfinite} {expr {isfinite(1e-314)}} 1 +test expr-54.7 {float classification: isfinite} {expr {isfinite(inf)}} 0 +test expr-54.8 {float classification: isfinite} {expr {isfinite(-inf)}} 0 +test expr-54.9 {float classification: isfinite} {expr {isfinite(NaN)}} 0 + +test expr-55.0 {float classification: isinf} {expr {isinf(1.0)}} 0 +test expr-55.1 {float classification: isinf} {expr {isinf(-1.0)}} 0 +test expr-55.2 {float classification: isinf} {expr {isinf(0.0)}} 0 +test expr-55.3 {float classification: isinf} {expr {isinf(-0.0)}} 0 +test expr-55.4 {float classification: isinf} {expr {isinf(1/Inf)}} 0 +test expr-55.5 {float classification: isinf} {expr {isinf(-1/Inf)}} 0 +test expr-55.6 {float classification: isinf} {expr {isinf(1e-314)}} 0 +test expr-55.7 {float classification: isinf} {expr {isinf(inf)}} 1 +test expr-55.8 {float classification: isinf} {expr {isinf(-inf)}} 1 +test expr-55.9 {float classification: isinf} {expr {isinf(NaN)}} 0 + +test expr-56.0 {float classification: isnan} {expr {isnan(1.0)}} 0 +test expr-56.1 {float classification: isnan} {expr {isnan(-1.0)}} 0 +test expr-56.2 {float classification: isnan} {expr {isnan(0.0)}} 0 +test expr-56.3 {float classification: isnan} {expr {isnan(-0.0)}} 0 +test expr-56.4 {float classification: isnan} {expr {isnan(1/Inf)}} 0 +test expr-56.5 {float classification: isnan} {expr {isnan(-1/Inf)}} 0 +test expr-56.6 {float classification: isnan} {expr {isnan(1e-314)}} 0 +test expr-56.7 {float classification: isnan} {expr {isnan(inf)}} 0 +test expr-56.8 {float classification: isnan} {expr {isnan(-inf)}} 0 +test expr-56.9 {float classification: isnan} {expr {isnan(NaN)}} 1 + +test expr-57.0 {float classification: isnormal} {expr {isnormal(1.0)}} 1 +test expr-57.1 {float classification: isnormal} {expr {isnormal(-1.0)}} 1 +test expr-57.2 {float classification: isnormal} {expr {isnormal(0.0)}} 0 +test expr-57.3 {float classification: isnormal} {expr {isnormal(-0.0)}} 0 +test expr-57.4 {float classification: isnormal} {expr {isnormal(1/Inf)}} 0 +test expr-57.5 {float classification: isnormal} {expr {isnormal(-1/Inf)}} 0 +test expr-57.6 {float classification: isnormal} {expr {isnormal(1e-314)}} 0 +test expr-57.7 {float classification: isnormal} {expr {isnormal(inf)}} 0 +test expr-57.8 {float classification: isnormal} {expr {isnormal(-inf)}} 0 +test expr-57.9 {float classification: isnormal} {expr {isnormal(NaN)}} 0 + +test expr-58.0 {float classification: issubnormal} {expr {issubnormal(1.0)}} 0 +test expr-58.1 {float classification: issubnormal} {expr {issubnormal(-1.0)}} 0 +test expr-58.2 {float classification: issubnormal} {expr {issubnormal(0.0)}} 0 +test expr-58.3 {float classification: issubnormal} {expr {issubnormal(-0.0)}} 0 +test expr-58.4 {float classification: issubnormal} {expr {issubnormal(1/Inf)}} 0 +test expr-58.5 {float classification: issubnormal} {expr {issubnormal(-1/Inf)}} 0 +test expr-58.6 {float classification: issubnormal} {expr {issubnormal(1e-314)}} 1 +test expr-58.7 {float classification: issubnormal} {expr {issubnormal(inf)}} 0 +test expr-58.8 {float classification: issubnormal} {expr {issubnormal(-inf)}} 0 +test expr-58.9 {float classification: issubnormal} {expr {issubnormal(NaN)}} 0 + +test expr-59.0 {float classification: fpclassify} {fpclassify 1.0} normal +test expr-59.1 {float classification: fpclassify} {fpclassify -1.0} normal +test expr-59.2 {float classification: fpclassify} {fpclassify 0.0} zero +test expr-59.3 {float classification: fpclassify} {fpclassify -0.0} zero +test expr-59.4 {float classification: fpclassify} {fpclassify [expr 1/Inf]} zero +test expr-59.5 {float classification: fpclassify} {fpclassify [expr -1/Inf]} zero +test expr-59.6 {float classification: fpclassify} {fpclassify 1e-314} subnormal +test expr-59.7 {float classification: fpclassify} {fpclassify inf} infinite +test expr-59.8 {float classification: fpclassify} {fpclassify -inf} infinite +test expr-59.9 {float classification: fpclassify} {fpclassify NaN} nan +test expr-59.10 {float classification: fpclassify} -returnCodes error -body { + fpclassify +} -result {wrong # args: should be "fpclassify floatValue"} +test expr-59.11 {float classification: fpclassify} -returnCodes error -body { + fpclassify a b +} -result {wrong # args: should be "fpclassify floatValue"} +test expr-59.12 {float classification: fpclassify} -returnCodes error -body { + fpclassify gorp +} -result {expected number but got "gorp"} + +test expr-60.1 {float classification: basic arg handling} -body { + expr isunordered() +} -returnCodes error -result {too few arguments for math function "isunordered"} +test expr-60.2 {float classification: basic arg handling} -body { + expr isunordered(1) +} -returnCodes error -result {too few arguments for math function "isunordered"} +test expr-60.3 {float classification: basic arg handling} -body { + expr {isunordered(1, 2, 3)} +} -returnCodes error -result {too many arguments for math function "isunordered"} +test expr-60.4 {float classification: basic arg handling} -body { + expr {isunordered(true, 1.0)} +} -returnCodes error -result {expected number but got "true"} +test expr-60.5 {float classification: basic arg handling} -body { + expr {isunordered("gorp", 1.0)} +} -returnCodes error -result {expected number but got "gorp"} +test expr-60.6 {float classification: basic arg handling} -body { + expr {isunordered(0x123, 1.0)} +} -match glob -result * +test expr-60.7 {float classification: basic arg handling} -body { + expr {isunordered(1.0, true)} +} -returnCodes error -result {expected number but got "true"} +test expr-60.8 {float classification: basic arg handling} -body { + expr {isunordered(1.0, "gorp")} +} -returnCodes error -result {expected number but got "gorp"} +test expr-60.9 {float classification: basic arg handling} -body { + expr {isunordered(1.0, 0x123)} +} -match glob -result * + +# Big matrix of comparisons, but it's just a binary isinf() +set values {1.0 -1.0 0.0 -0.0 1e-314 Inf -Inf NaN} +set results {0 0 0 0 0 0 0 1} +set ctr 0 +foreach v1 $values r1 $results { + foreach v2 $values r2 $results { + test expr-61.[incr ctr] "float classification: isunordered($v1,$v2)" { + expr {isunordered($v1, $v2)} + } [expr {$r1 || $r2}] + } +} +unset -nocomplain values results ctr + +# cleanup +unset -nocomplain a +unset -nocomplain min +unset -nocomplain max ::tcltest::cleanupTests return # Local Variables: # mode: tcl # End: Index: tests/info.test ================================================================== --- tests/info.test +++ tests/info.test @@ -653,11 +653,11 @@ namespace eval x info vars foo } -cleanup { namespace delete x } -result {} -set functions {abs acos asin atan atan2 bool ceil cos cosh double entier exp floor fmod hypot int isqrt log log10 max min pow rand round sin sinh sqrt srand tan tanh wide} +set functions {abs acos asin atan atan2 bool ceil cos cosh double entier exp floor fmod hypot int isfinite isinf isnan isnormal isqrt issubnormal isunordered log log10 max min pow rand round sin sinh sqrt srand tan tanh wide} # Check whether the extra testing functions are defined... if {!([catch {expr T1()} msg] && ($msg eq {invalid command name "tcl::mathfunc::T1"}))} { set functions "T1 T2 T3 $functions" ;# A lazy way of prepending! } test info-20.1 {info functions option} {info functions sin} sin