diff options
| author | sebres <sebres@users.sourceforge.net> | 2019-03-05 18:23:26 (GMT) |
|---|---|---|
| committer | sebres <sebres@users.sourceforge.net> | 2019-03-05 18:23:26 (GMT) |
| commit | f456cd9b3e6743bf15cd756bfc116153fa95605c (patch) | |
| tree | 85e4e3121d93802d9d5df50157ead27f68ff7a43 /generic | |
| parent | 8c315fd31ff823b217374dd32577e04c42674249 (diff) | |
| parent | b930511d5b774c13f2cd22d7820ad0acf7069c39 (diff) | |
| download | tcl-f456cd9b3e6743bf15cd756bfc116153fa95605c.zip tcl-f456cd9b3e6743bf15cd756bfc116153fa95605c.tar.gz tcl-f456cd9b3e6743bf15cd756bfc116153fa95605c.tar.bz2 | |
merge 8.7 (TIP#527, New measurement facilities in TCL: New command timerate, performance test suite)
Diffstat (limited to 'generic')
| -rw-r--r-- | generic/tclBasic.c | 17 | ||||
| -rw-r--r-- | generic/tclClock.c | 9 | ||||
| -rw-r--r-- | generic/tclCmdMZ.c | 387 | ||||
| -rw-r--r-- | generic/tclInt.h | 21 | ||||
| -rw-r--r-- | generic/tclObj.c | 109 | ||||
| -rw-r--r-- | generic/tclPort.h | 3 | ||||
| -rw-r--r-- | generic/tclStrToD.c | 138 | ||||
| -rw-r--r-- | generic/tclTest.c | 4 | ||||
| -rw-r--r-- | generic/tclUtf.c | 4 |
9 files changed, 512 insertions, 180 deletions
diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 2a23ed0..5ad060b 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -300,6 +300,7 @@ static const CmdInfo builtInCmds[] = { {"source", Tcl_SourceObjCmd, NULL, TclNRSourceObjCmd, 0}, {"tell", Tcl_TellObjCmd, NULL, NULL, CMD_IS_SAFE}, {"time", Tcl_TimeObjCmd, NULL, NULL, CMD_IS_SAFE}, + {"timerate", Tcl_TimeRateObjCmd, NULL, NULL, CMD_IS_SAFE}, {"unload", Tcl_UnloadObjCmd, NULL, NULL, 0}, {"update", Tcl_UpdateObjCmd, NULL, NULL, CMD_IS_SAFE}, {"vwait", Tcl_VwaitObjCmd, NULL, NULL, CMD_IS_SAFE}, @@ -540,7 +541,7 @@ Tcl_CreateInterp(void) const BuiltinFuncDef *builtinFuncPtr; const OpCmdInfo *opcmdInfoPtr; const CmdInfo *cmdInfoPtr; - Tcl_Namespace *mathfuncNSPtr, *mathopNSPtr; + Tcl_Namespace *nsPtr; Tcl_HashEntry *hPtr; int isNew; CancelInfo *cancelInfo; @@ -950,8 +951,8 @@ Tcl_CreateInterp(void) * Register the builtin math functions. */ - mathfuncNSPtr = Tcl_CreateNamespace(interp, "::tcl::mathfunc", NULL,NULL); - if (mathfuncNSPtr == NULL) { + nsPtr = Tcl_CreateNamespace(interp, "::tcl::mathfunc", NULL,NULL); + if (nsPtr == NULL) { Tcl_Panic("Can't create math function namespace"); } #define MATH_FUNC_PREFIX_LEN 17 /* == strlen("::tcl::mathfunc::") */ @@ -961,18 +962,18 @@ Tcl_CreateInterp(void) strcpy(mathFuncName+MATH_FUNC_PREFIX_LEN, builtinFuncPtr->name); Tcl_CreateObjCommand(interp, mathFuncName, builtinFuncPtr->objCmdProc, builtinFuncPtr->clientData, NULL); - Tcl_Export(interp, mathfuncNSPtr, builtinFuncPtr->name, 0); + Tcl_Export(interp, nsPtr, builtinFuncPtr->name, 0); } /* * Register the mathematical "operator" commands. [TIP #174] */ - mathopNSPtr = Tcl_CreateNamespace(interp, "::tcl::mathop", NULL, NULL); - if (mathopNSPtr == NULL) { + nsPtr = Tcl_CreateNamespace(interp, "::tcl::mathop", NULL, NULL); + if (nsPtr == NULL) { Tcl_Panic("can't create math operator namespace"); } - Tcl_Export(interp, mathopNSPtr, "*", 1); + Tcl_Export(interp, nsPtr, "*", 1); #define MATH_OP_PREFIX_LEN 15 /* == strlen("::tcl::mathop::") */ memcpy(mathFuncName, "::tcl::mathop::", MATH_OP_PREFIX_LEN); for (opcmdInfoPtr=mathOpCmds ; opcmdInfoPtr->name!=NULL ; opcmdInfoPtr++){ @@ -6821,7 +6822,7 @@ ExprIsqrtFunc( if (Tcl_GetBignumFromObj(interp, objv[1], &big) != TCL_OK) { return TCL_ERROR; } - if (SIGN(&big) == MP_NEG) { + if (mp_isneg(&big)) { mp_clear(&big); goto negarg; } diff --git a/generic/tclClock.c b/generic/tclClock.c index 634ab1b..c5677a5 100644 --- a/generic/tclClock.c +++ b/generic/tclClock.c @@ -1772,8 +1772,7 @@ ClockClicksObjCmd( #endif break; case CLICKS_MICROS: - Tcl_GetTime(&now); - clicks = ((Tcl_WideInt) now.sec * 1000000) + now.usec; + clicks = TclpGetMicroseconds(); break; } @@ -1843,15 +1842,11 @@ ClockMicrosecondsObjCmd( int objc, /* Parameter count */ Tcl_Obj *const *objv) /* Parameter values */ { - Tcl_Time now; - if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, NULL); return TCL_ERROR; } - Tcl_GetTime(&now); - Tcl_SetObjResult(interp, Tcl_NewWideIntObj( - ((Tcl_WideInt) now.sec * 1000000) + now.usec)); + Tcl_SetObjResult(interp, Tcl_NewWideIntObj(TclpGetMicroseconds())); return TCL_OK; } diff --git a/generic/tclCmdMZ.c b/generic/tclCmdMZ.c index b954787..4317a6a 100644 --- a/generic/tclCmdMZ.c +++ b/generic/tclCmdMZ.c @@ -17,6 +17,7 @@ */ #include "tclInt.h" +#include "tclCompile.h" #include "tclRegexp.h" #include "tclStringTrim.h" @@ -4042,7 +4043,7 @@ Tcl_TimeObjCmd( start = TclpGetWideClicks(); #endif while (i-- > 0) { - result = Tcl_EvalObjEx(interp, objPtr, 0); + result = TclEvalObjEx(interp, objPtr, 0, NULL, 0); if (result != TCL_OK) { return result; } @@ -4082,6 +4083,390 @@ Tcl_TimeObjCmd( /* *---------------------------------------------------------------------- * + * Tcl_TimeRateObjCmd -- + * + * This object-based procedure is invoked to process the "timerate" Tcl + * command. + * This is similar to command "time", except the execution limited by + * given time (in milliseconds) instead of repetition count. + * + * Example: + * timerate {after 5} 1000 ; # equivalent for `time {after 5} [expr 1000/5]` + * + * Results: + * A standard Tcl object result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_TimeRateObjCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + static + double measureOverhead = 0; /* global measure-overhead */ + double overhead = -1; /* given measure-overhead */ + register Tcl_Obj *objPtr; + register int result, i; + Tcl_Obj *calibrate = NULL, *direct = NULL; + Tcl_WideUInt count = 0; /* Holds repetition count */ + Tcl_WideInt maxms = WIDE_MIN; + /* Maximal running time (in milliseconds) */ + Tcl_WideUInt maxcnt = WIDE_MAX; + /* Maximal count of iterations. */ + Tcl_WideUInt threshold = 1; /* Current threshold for check time (faster + * repeat count without time check) */ + Tcl_WideUInt maxIterTm = 1; /* Max time of some iteration as max threshold + * additionally avoid divide to zero (never < 1) */ + unsigned short factor = 50; /* Factor (4..50) limiting threshold to avoid + * growth of execution time. */ + register Tcl_WideInt start, middle, stop; +#ifndef TCL_WIDE_CLICKS + Tcl_Time now; +#endif + + static const char *const options[] = { + "-direct", "-overhead", "-calibrate", "--", NULL + }; + enum options { + TMRT_EV_DIRECT, TMRT_OVERHEAD, TMRT_CALIBRATE, TMRT_LAST + }; + + NRE_callback *rootPtr; + ByteCode *codePtr = NULL; + + for (i = 1; i < objc - 1; i++) { + int index; + if (Tcl_GetIndexFromObj(NULL, objv[i], options, "option", TCL_EXACT, + &index) != TCL_OK) { + break; + } + if (index == TMRT_LAST) { + i++; + break; + } + switch (index) { + case TMRT_EV_DIRECT: + direct = objv[i]; + break; + case TMRT_OVERHEAD: + if (++i >= objc - 1) { + goto usage; + } + if (Tcl_GetDoubleFromObj(interp, objv[i], &overhead) != TCL_OK) { + return TCL_ERROR; + } + break; + case TMRT_CALIBRATE: + calibrate = objv[i]; + break; + } + } + + if (i >= objc || i < objc-3) { +usage: + Tcl_WrongNumArgs(interp, 1, objv, "?-direct? ?-calibrate? ?-overhead double? command ?time ?max-count??"); + return TCL_ERROR; + } + objPtr = objv[i++]; + if (i < objc) { /* max-time */ + result = Tcl_GetWideIntFromObj(interp, objv[i++], &maxms); + if (result != TCL_OK) { + return result; + } + if (i < objc) { /* max-count*/ + Tcl_WideInt v; + result = Tcl_GetWideIntFromObj(interp, objv[i], &v); + if (result != TCL_OK) { + return result; + } + maxcnt = (v > 0) ? v : 0; + } + } + + /* if calibrate */ + if (calibrate) { + + /* if no time specified for the calibration */ + if (maxms == WIDE_MIN) { + Tcl_Obj *clobjv[6]; + Tcl_WideInt maxCalTime = 5000; + double lastMeasureOverhead = measureOverhead; + + clobjv[0] = objv[0]; + i = 1; + if (direct) { + clobjv[i++] = direct; + } + clobjv[i++] = objPtr; + + /* reset last measurement overhead */ + measureOverhead = (double)0; + + /* self-call with 100 milliseconds to warm-up, + * before entering the calibration cycle */ + TclNewIntObj(clobjv[i], 100); + Tcl_IncrRefCount(clobjv[i]); + result = Tcl_TimeRateObjCmd(dummy, interp, i+1, clobjv); + Tcl_DecrRefCount(clobjv[i]); + if (result != TCL_OK) { + return result; + } + + i--; + clobjv[i++] = calibrate; + clobjv[i++] = objPtr; + + /* set last measurement overhead to max */ + measureOverhead = (double)UWIDE_MAX; + + /* calibration cycle until it'll be preciser */ + maxms = -1000; + do { + lastMeasureOverhead = measureOverhead; + TclNewIntObj(clobjv[i], (int)maxms); + Tcl_IncrRefCount(clobjv[i]); + result = Tcl_TimeRateObjCmd(dummy, interp, i+1, clobjv); + Tcl_DecrRefCount(clobjv[i]); + if (result != TCL_OK) { + return result; + } + maxCalTime += maxms; + /* increase maxms for preciser calibration */ + maxms -= (-maxms / 4); + /* as long as new value more as 0.05% better */ + } while ( (measureOverhead >= lastMeasureOverhead + || measureOverhead / lastMeasureOverhead <= 0.9995) + && maxCalTime > 0 + ); + + return result; + } + if (maxms == 0) { + /* reset last measurement overhead */ + measureOverhead = 0; + Tcl_SetObjResult(interp, Tcl_NewLongObj(0)); + return TCL_OK; + } + + /* if time is negative - make current overhead more precise */ + if (maxms > 0) { + /* set last measurement overhead to max */ + measureOverhead = (double)UWIDE_MAX; + } else { + maxms = -maxms; + } + + } + + if (maxms == WIDE_MIN) { + maxms = 1000; + } + if (overhead == -1) { + overhead = measureOverhead; + } + + /* be sure that resetting of result will not smudge the further measurement */ + Tcl_ResetResult(interp); + + /* compile object */ + if (!direct) { + if (TclInterpReady(interp) != TCL_OK) { + return TCL_ERROR; + } + codePtr = TclCompileObj(interp, objPtr, NULL, 0); + TclPreserveByteCode(codePtr); + } + + /* get start and stop time */ +#ifdef TCL_WIDE_CLICKS + start = middle = TclpGetWideClicks(); + /* time to stop execution (in wide clicks) */ + stop = start + (maxms * 1000 / TclpWideClickInMicrosec()); +#else + Tcl_GetTime(&now); + start = now.sec; start *= 1000000; start += now.usec; + middle = start; + /* time to stop execution (in microsecs) */ + stop = start + maxms * 1000; +#endif + + /* start measurement */ + if (maxcnt > 0) + while (1) { + /* eval single iteration */ + count++; + + if (!direct) { + /* precompiled */ + rootPtr = TOP_CB(interp); + result = TclNRExecuteByteCode(interp, codePtr); + result = TclNRRunCallbacks(interp, result, rootPtr); + } else { + /* eval */ + result = TclEvalObjEx(interp, objPtr, 0, NULL, 0); + } + if (result != TCL_OK) { + /* allow break from measurement cycle (used for conditional stop) */ + if (result != TCL_BREAK) { + goto done; + } + /* force stop immediately */ + threshold = 1; + maxcnt = 0; + result = TCL_OK; + } + + /* don't check time up to threshold */ + if (--threshold > 0) continue; + + /* check stop time reached, estimate new threshold */ + #ifdef TCL_WIDE_CLICKS + middle = TclpGetWideClicks(); + #else + Tcl_GetTime(&now); + middle = now.sec; middle *= 1000000; middle += now.usec; + #endif + if (middle >= stop || count >= maxcnt) { + break; + } + + /* don't calculate threshold by few iterations, because sometimes first + * iteration(s) can be too fast or slow (cached, delayed clean up, etc) */ + if (count < 10) { + threshold = 1; continue; + } + + /* average iteration time in microsecs */ + threshold = (middle - start) / count; + if (threshold > maxIterTm) { + maxIterTm = threshold; + /* interations seems to be longer */ + if (threshold > (maxIterTm * 2)) { + if ((factor *= 2) > 50) factor = 50; + } else { + if (factor < 50) factor++; + } + } else if (factor > 4) { + /* interations seems to be shorter */ + if (threshold < (maxIterTm / 2)) { + if ((factor /= 2) < 4) factor = 4; + } else { + factor--; + } + } + /* as relation between remaining time and time since last check, + * maximal some % of time (by factor), so avoid growing of the execution time + * if iterations are not consistent, e. g. wax continuously on time) */ + threshold = ((stop - middle) / maxIterTm) / factor + 1; + if (threshold > 100000) { /* fix for too large threshold */ + threshold = 100000; + } + /* consider max-count */ + if (threshold > maxcnt - count) { + threshold = maxcnt - count; + } + } + + { + Tcl_Obj *objarr[8], **objs = objarr; + Tcl_WideInt val; + const char *fmt; + + middle -= start; /* execution time in microsecs */ + + #ifdef TCL_WIDE_CLICKS + /* convert execution time in wide clicks to microsecs */ + middle *= TclpWideClickInMicrosec(); + #endif + + /* if not calibrate */ + if (!calibrate) { + /* minimize influence of measurement overhead */ + if (overhead > 0) { + /* estimate the time of overhead (microsecs) */ + Tcl_WideUInt curOverhead = overhead * count; + if (middle > (Tcl_WideInt)curOverhead) { + middle -= curOverhead; + } else { + middle = 0; + } + } + } else { + /* calibration - obtaining new measurement overhead */ + if (measureOverhead > (double)middle / count) { + measureOverhead = (double)middle / count; + } + objs[0] = Tcl_NewDoubleObj(measureOverhead); + TclNewLiteralStringObj(objs[1], "\xC2\xB5s/#-overhead"); /* mics */ + objs += 2; + } + + val = middle / count; /* microsecs per iteration */ + if (val >= 1000000) { + objs[0] = Tcl_NewWideIntObj(val); + } else { + if (val < 10) { fmt = "%.6f"; } else + if (val < 100) { fmt = "%.4f"; } else + if (val < 1000) { fmt = "%.3f"; } else + if (val < 10000) { fmt = "%.2f"; } else + { fmt = "%.1f"; }; + objs[0] = Tcl_ObjPrintf(fmt, ((double)middle)/count); + } + + objs[2] = Tcl_NewWideIntObj(count); /* iterations */ + + /* calculate speed as rate (count) per sec */ + if (!middle) middle++; /* +1 ms, just to avoid divide by zero */ + if (count < (WIDE_MAX / 1000000)) { + val = (count * 1000000) / middle; + if (val < 100000) { + if (val < 100) { fmt = "%.3f"; } else + if (val < 1000) { fmt = "%.2f"; } else + { fmt = "%.1f"; }; + objs[4] = Tcl_ObjPrintf(fmt, ((double)(count * 1000000)) / middle); + } else { + objs[4] = Tcl_NewWideIntObj(val); + } + } else { + objs[4] = Tcl_NewWideIntObj((count / middle) * 1000000); + } + + /* estimated net execution time (in millisecs) */ + if (!calibrate) { + objs[6] = Tcl_ObjPrintf("%.3f", (double)middle / 1000); + TclNewLiteralStringObj(objs[7], "nett-ms"); + } + + /* + * Construct the result as a list because many programs have always parsed + * as such (extracting the first element, typically). + */ + + TclNewLiteralStringObj(objs[1], "\xC2\xB5s/#"); /* mics/# */ + TclNewLiteralStringObj(objs[3], "#"); + TclNewLiteralStringObj(objs[5], "#/sec"); + Tcl_SetObjResult(interp, Tcl_NewListObj(8, objarr)); + } + +done: + + if (codePtr != NULL) { + TclReleaseByteCode(codePtr); + } + + return result; +} + +/* + *---------------------------------------------------------------------- + * * Tcl_TryObjCmd, TclNRTryObjCmd -- * * This procedure is invoked to process the "try" Tcl command. See the diff --git a/generic/tclInt.h b/generic/tclInt.h index 6bd1674..d999603 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -2807,8 +2807,6 @@ struct Tcl_LoadHandle_ { #define TCL_DD_SHORTEST 0x4 /* Use the shortest possible string */ -#define TCL_DD_STEELE 0x5 - /* Use the original Steele&White algorithm */ #define TCL_DD_E_FORMAT 0x2 /* Use a fixed-length string of digits, * suitable for E format*/ @@ -2824,10 +2822,6 @@ struct Tcl_LoadHandle_ { #define TCL_DD_CONVERSION_TYPE_MASK 0x3 /* Mask to isolate the conversion type */ -#define TCL_DD_STEELE0 0x1 - /* 'Steele&White' after masking */ -#define TCL_DD_SHORTEST0 0x0 - /* 'Shortest possible' after masking */ /* *---------------------------------------------------------------- @@ -3194,10 +3188,22 @@ MODULE_SCOPE int TclpLoadMemory(Tcl_Interp *interp, void *buffer, MODULE_SCOPE void TclInitThreadStorage(void); MODULE_SCOPE void TclFinalizeThreadDataThread(void); MODULE_SCOPE void TclFinalizeThreadStorage(void); + #ifdef TCL_WIDE_CLICKS MODULE_SCOPE Tcl_WideInt TclpGetWideClicks(void); MODULE_SCOPE double TclpWideClicksToNanoseconds(Tcl_WideInt clicks); +MODULE_SCOPE double TclpWideClickInMicrosec(void); +#else +# ifdef _WIN32 +# define TCL_WIDE_CLICKS 1 +MODULE_SCOPE Tcl_WideInt TclpGetWideClicks(void); +MODULE_SCOPE double TclpWideClickInMicrosec(void); +# define TclpWideClicksToNanoseconds(clicks) \ + ((double)(clicks) * TclpWideClickInMicrosec() * 1000) +# endif #endif +MODULE_SCOPE Tcl_WideInt TclpGetMicroseconds(void); + MODULE_SCOPE int TclZlibInit(Tcl_Interp *interp); MODULE_SCOPE void * TclpThreadCreateKey(void); MODULE_SCOPE void TclpThreadDeleteKey(void *keyPtr); @@ -3468,6 +3474,9 @@ MODULE_SCOPE int Tcl_ThrowObjCmd(void *dummy, Tcl_Interp *interp, MODULE_SCOPE int Tcl_TimeObjCmd(void *clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); +MODULE_SCOPE int Tcl_TimeRateObjCmd(void *clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); MODULE_SCOPE int Tcl_TraceObjCmd(void *clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); diff --git a/generic/tclObj.c b/generic/tclObj.c index 5cf35b4..089945e 100644 --- a/generic/tclObj.c +++ b/generic/tclObj.c @@ -2711,27 +2711,23 @@ Tcl_GetLongFromObj( */ mp_int big; + unsigned long scratch, value = 0, numBytes = sizeof(unsigned long); + unsigned char *bytes = (unsigned char *) &scratch; UNPACK_BIGNUM(objPtr, big); - if ((size_t) big.used <= (CHAR_BIT * sizeof(unsigned long) + DIGIT_BIT - 1) - / DIGIT_BIT) { - unsigned long scratch, value = 0, numBytes = sizeof(unsigned long); - unsigned char *bytes = (unsigned char *) &scratch; - - if (mp_to_unsigned_bin_n(&big, bytes, &numBytes) == MP_OKAY) { - while (numBytes-- > 0) { + if (mp_to_unsigned_bin_n(&big, bytes, &numBytes) == MP_OKAY) { + while (numBytes-- > 0) { value = (value << CHAR_BIT) | *bytes++; + } + if (big.sign) { + if (value <= 1 + (unsigned long)LONG_MAX) { + *longPtr = - (long) value; + return TCL_OK; } - if (big.sign) { - if (value <= 1 + (unsigned long)LONG_MAX) { - *longPtr = - (long) value; - return TCL_OK; - } - } else { - if (value <= (unsigned long)ULONG_MAX) { - *longPtr = (long) value; - return TCL_OK; - } + } else { + if (value <= (unsigned long)ULONG_MAX) { + *longPtr = (long) value; + return TCL_OK; } } } @@ -2953,29 +2949,25 @@ Tcl_GetWideIntFromObj( */ mp_int big; + Tcl_WideUInt value = 0; + unsigned long numBytes = sizeof(Tcl_WideInt); + Tcl_WideInt scratch; + unsigned char *bytes = (unsigned char *) &scratch; UNPACK_BIGNUM(objPtr, big); - if ((size_t) big.used <= (CHAR_BIT * sizeof(Tcl_WideInt) - + DIGIT_BIT - 1) / DIGIT_BIT) { - Tcl_WideUInt value = 0; - unsigned long numBytes = sizeof(Tcl_WideInt); - Tcl_WideInt scratch; - unsigned char *bytes = (unsigned char *) &scratch; - - if (mp_to_unsigned_bin_n(&big, bytes, &numBytes) == MP_OKAY) { - while (numBytes-- > 0) { - value = (value << CHAR_BIT) | *bytes++; + if (mp_to_unsigned_bin_n(&big, bytes, &numBytes) == MP_OKAY) { + while (numBytes-- > 0) { + value = (value << CHAR_BIT) | *bytes++; + } + if (big.sign) { + if (value <= 1 + ~(Tcl_WideUInt)WIDE_MIN) { + *wideIntPtr = - (Tcl_WideInt) value; + return TCL_OK; } - if (big.sign) { - if (value <= 1 + ~(Tcl_WideUInt)WIDE_MIN) { - *wideIntPtr = - (Tcl_WideInt) value; - return TCL_OK; - } - } else { - if (value <= (Tcl_WideUInt)WIDE_MAX) { - *wideIntPtr = (Tcl_WideInt) value; - return TCL_OK; - } + } else { + if (value <= (Tcl_WideUInt)WIDE_MAX) { + *wideIntPtr = (Tcl_WideInt) value; + return TCL_OK; } } } @@ -3412,33 +3404,30 @@ Tcl_SetBignumObj( Tcl_Obj *objPtr, /* Object to set */ mp_int *bignumValue) /* Value to store */ { + Tcl_WideUInt value = 0; + unsigned long numBytes = sizeof(Tcl_WideUInt); + Tcl_WideUInt scratch; + unsigned char *bytes = (unsigned char *) &scratch; + if (Tcl_IsShared(objPtr)) { Tcl_Panic("%s called with shared object", "Tcl_SetBignumObj"); } - if ((size_t) bignumValue->used - <= (CHAR_BIT * sizeof(Tcl_WideUInt) + DIGIT_BIT - 1) / DIGIT_BIT) { - Tcl_WideUInt value = 0; - unsigned long numBytes = sizeof(Tcl_WideUInt); - Tcl_WideUInt scratch; - unsigned char *bytes = (unsigned char *) &scratch; - - if (mp_to_unsigned_bin_n(bignumValue, bytes, &numBytes) != MP_OKAY) { - goto tooLargeForWide; - } - while (numBytes-- > 0) { - value = (value << CHAR_BIT) | *bytes++; - } - if (value > ((Tcl_WideUInt)WIDE_MAX + bignumValue->sign)) { - goto tooLargeForWide; - } - if (bignumValue->sign) { - TclSetIntObj(objPtr, -(Tcl_WideInt)value); - } else { - TclSetIntObj(objPtr, (Tcl_WideInt)value); - } - mp_clear(bignumValue); - return; + if (mp_to_unsigned_bin_n(bignumValue, bytes, &numBytes) != MP_OKAY) { + goto tooLargeForWide; + } + while (numBytes-- > 0) { + value = (value << CHAR_BIT) | *bytes++; + } + if (value > ((Tcl_WideUInt)WIDE_MAX + bignumValue->sign)) { + goto tooLargeForWide; + } + if (bignumValue->sign) { + TclSetIntObj(objPtr, -(Tcl_WideInt)value); + } else { + TclSetIntObj(objPtr, (Tcl_WideInt)value); } + mp_clear(bignumValue); + return; tooLargeForWide: TclInvalidateStringRep(objPtr); TclFreeIntRep(objPtr); diff --git a/generic/tclPort.h b/generic/tclPort.h index d3f6233..58e1f5e 100644 --- a/generic/tclPort.h +++ b/generic/tclPort.h @@ -27,5 +27,8 @@ #define UWIDE_MAX ((Tcl_WideUInt)-1) #define WIDE_MAX ((Tcl_WideInt)(UWIDE_MAX >> 1)) #define WIDE_MIN ((Tcl_WideInt)((Tcl_WideUInt)WIDE_MAX+1)) +#define UWIDE_MAX ((Tcl_WideUInt)-1) +#define WIDE_MAX ((Tcl_WideInt)(UWIDE_MAX >> 1)) +#define WIDE_MIN ((Tcl_WideInt)((Tcl_WideUInt)WIDE_MAX+1)) #endif /* _TCLPORT */ diff --git a/generic/tclStrToD.c b/generic/tclStrToD.c index 9c8c0d4..f118c5c 100644 --- a/generic/tclStrToD.c +++ b/generic/tclStrToD.c @@ -320,36 +320,36 @@ static char * StrictQuickFormat(double, int, int, double, static char * QuickConversion(double, int, int, int, int, int, int, int *, char **); static void CastOutPowersOf2(int *, int *, int *); -static char * ShorteningInt64Conversion(Double *, int, Tcl_WideUInt, +static char * ShorteningInt64Conversion(Double *, Tcl_WideUInt, int, int, int, int, int, int, int, int, int, int, int, int *, char **); -static char * StrictInt64Conversion(Double *, int, Tcl_WideUInt, +static char * StrictInt64Conversion(Double *, Tcl_WideUInt, int, int, int, int, int, int, int, int, int *, char **); static int ShouldBankerRoundUpPowD(mp_int *, int, int); static int ShouldBankerRoundUpToNextPowD(mp_int *, mp_int *, - int, int, int, mp_int *); + int, int, mp_int *); static char * ShorteningBignumConversionPowD(Double *dPtr, - int convType, Tcl_WideUInt bw, int b2, int b5, + Tcl_WideUInt bw, int b2, int b5, int m2plus, int m2minus, int m5, int sd, int k, int len, int ilim, int ilim1, int *decpt, char **endPtr); -static char * StrictBignumConversionPowD(Double *dPtr, int convType, +static char * StrictBignumConversionPowD(Double *dPtr, Tcl_WideUInt bw, int b2, int b5, int sd, int k, int len, int ilim, int ilim1, int *decpt, char **endPtr); static int ShouldBankerRoundUp(mp_int *, mp_int *, int); static int ShouldBankerRoundUpToNext(mp_int *, mp_int *, - mp_int *, int, int, mp_int *); -static char * ShorteningBignumConversion(Double *dPtr, int convType, + mp_int *, int); +static char * ShorteningBignumConversion(Double *dPtr, Tcl_WideUInt bw, int b2, int m2plus, int m2minus, int s2, int s5, int k, int len, int ilim, int ilim1, int *decpt, char **endPtr); -static char * StrictBignumConversion(Double *dPtr, int convType, +static char * StrictBignumConversion(Double *dPtr, Tcl_WideUInt bw, int b2, int s2, int s5, int k, int len, int ilim, int ilim1, int *decpt, @@ -2347,9 +2347,8 @@ ComputeScale( static inline void SetPrecisionLimits( - int convType, /* Type of conversion: TCL_DD_SHORTEST, - * TCL_DD_STEELE0, TCL_DD_E_FMT, - * TCL_DD_F_FMT. */ + int flags, /* Type of conversion: TCL_DD_SHORTEST, + * TCL_DD_E_FMT, TCL_DD_F_FMT. */ int k, /* Floor(log10(number to convert)) */ int *ndigitsPtr, /* IN/OUT: Number of digits requested (will be * adjusted if needed). */ @@ -2359,13 +2358,7 @@ SetPrecisionLimits( int *iLim1Ptr) /* OUT: Number of digits of significance if * the quick method is used. */ { - switch (convType) { - case TCL_DD_SHORTEST0: - case TCL_DD_STEELE0: - *iLimPtr = *iLim1Ptr = -1; - *iPtr = 18; - *ndigitsPtr = 0; - break; + switch (flags & TCL_DD_CONVERSION_TYPE_MASK) { case TCL_DD_E_FORMAT: if (*ndigitsPtr <= 0) { *ndigitsPtr = 1; @@ -2381,10 +2374,10 @@ SetPrecisionLimits( } break; default: - *iPtr = -1; - *iLimPtr = -1; - *iLim1Ptr = -1; - Tcl_Panic("impossible conversion type in TclDoubleDigits"); + *iLimPtr = *iLim1Ptr = -1; + *iPtr = 18; + *ndigitsPtr = 0; + break; } } @@ -2814,8 +2807,6 @@ CastOutPowersOf2( static inline char * ShorteningInt64Conversion( Double *dPtr, /* Original number to convert. */ - int convType, /* Type of conversion (shortest, Steele, - * E format, F format). */ Tcl_WideUInt bw, /* Integer significand. */ int b2, int b5, /* Scale factor for the significand in the * numerator. */ @@ -2882,7 +2873,7 @@ ShorteningInt64Conversion( */ if (b < mplus || (b == mplus - && convType != TCL_DD_STEELE0 && (dPtr->w.word1 & 1) == 0)) { + && (dPtr->w.word1 & 1) == 0)) { /* * Make sure we shouldn't be rounding *up* instead, in case the * next number above is closer. @@ -2911,7 +2902,7 @@ ShorteningInt64Conversion( */ if (b > S - mminus || (b == S - mminus - && convType != TCL_DD_STEELE0 && (dPtr->w.word1 & 1) == 0)) { + && (dPtr->w.word1 & 1) == 0)) { if (digit == 9) { *s++ = '9'; s = BumpUp(s, retval, &k); @@ -2983,8 +2974,6 @@ ShorteningInt64Conversion( static inline char * StrictInt64Conversion( Double *dPtr, /* Original number to convert. */ - int convType, /* Type of conversion (shortest, Steele, - * E format, F format). */ Tcl_WideUInt bw, /* Integer significand. */ int b2, int b5, /* Scale factor for the significand in the * numerator. */ @@ -3093,7 +3082,7 @@ ShouldBankerRoundUpPowD( int isodd) /* 1 if the digit is odd, 0 if even. */ { int i; - static const mp_digit topbit = 1 << (DIGIT_BIT - 1); + static const mp_digit topbit = ((mp_digit)1) << (DIGIT_BIT - 1); if (b->used < sd || (b->dp[sd-1] & topbit) == 0) { return 0; @@ -3129,9 +3118,6 @@ ShouldBankerRoundUpToNextPowD( mp_int *b, /* Numerator of the fraction. */ mp_int *m, /* Numerator of the rounding tolerance. */ int sd, /* Common denominator is 2**(sd*DIGIT_BIT). */ - int convType, /* Conversion type: STEELE defeats - * round-to-even (not sure why one wants to do - * this; I copied it from Gay). FIXME */ int isodd, /* 1 if the integer significand is odd. */ mp_int *temp) /* Work area for the calculation. */ { @@ -3157,10 +3143,6 @@ ShouldBankerRoundUpToNextPowD( return 1; } } - if (convType == TCL_DD_STEELE0) { - /* Biased rounding. */ - return 0; - } return isodd; } @@ -3190,8 +3172,6 @@ ShouldBankerRoundUpToNextPowD( static inline char * ShorteningBignumConversionPowD( Double *dPtr, /* Original number to convert. */ - int convType, /* Type of conversion (shortest, Steele, - * E format, F format). */ Tcl_WideUInt bw, /* Integer significand. */ int b2, int b5, /* Scale factor for the significand in the * numerator. */ @@ -3277,7 +3257,7 @@ ShorteningBignumConversionPowD( r1 = mp_cmp_mag(&b, (m2plus > m2minus)? &mplus : &mminus); if (r1 == MP_LT || (r1 == MP_EQ - && convType != TCL_DD_STEELE0 && (dPtr->w.word1 & 1) == 0)) { + && (dPtr->w.word1 & 1) == 0)) { /* * Make sure we shouldn't be rounding *up* instead, in case the * next number above is closer. @@ -3305,7 +3285,7 @@ ShorteningBignumConversionPowD( * number? */ - if (ShouldBankerRoundUpToNextPowD(&b, &mminus, sd, convType, + if (ShouldBankerRoundUpToNextPowD(&b, &mminus, sd, dPtr->w.word1 & 1, &temp)) { if (digit == 9) { *s++ = '9'; @@ -3383,8 +3363,6 @@ ShorteningBignumConversionPowD( static inline char * StrictBignumConversionPowD( Double *dPtr, /* Original number to convert. */ - int convType, /* Type of conversion (shortest, Steele, - * E format, F format). */ Tcl_WideUInt bw, /* Integer significand. */ int b2, int b5, /* Scale factor for the significand in the * numerator. */ @@ -3405,7 +3383,6 @@ StrictBignumConversionPowD( mp_digit digit; /* Current output digit. */ char *s = retval; /* Cursor in the output buffer. */ int i; /* Index in the output buffer. */ - mp_int temp; /* * b = bw * 2**b2 * 5**b5 @@ -3424,7 +3401,6 @@ StrictBignumConversionPowD( ilim = ilim1; --k; } - mp_init(&temp); /* * Loop through the digits. Do division and mod by s == 2**(sd*DIGIT_BIT) @@ -3473,7 +3449,7 @@ StrictBignumConversionPowD( * string. */ - mp_clear_multi(&b, &temp, NULL); + mp_clear(&b); *s = '\0'; *decpt = k; if (endPtr) { @@ -3537,29 +3513,24 @@ ShouldBankerRoundUpToNext( * the last digit. */ mp_int *m, /* Numerator of the rounding tolerance. */ mp_int *S, /* Denominator. */ - int convType, /* Conversion type: STEELE0 defeats - * round-to-even. (Not sure why one would want - * this; I coped it from Gay). FIXME */ - int isodd, /* 1 if the integer significand is odd. */ - mp_int *temp) /* Work area needed for the calculation. */ + int isodd) /* 1 if the integer significand is odd. */ { int r; + mp_int temp; /* * Compare b and S-m: this is the same as comparing B+m and S. */ - mp_add(b, m, temp); - r = mp_cmp_mag(temp, S); + mp_init(&temp); + mp_add(b, m, &temp); + r = mp_cmp_mag(&temp, S); + mp_clear(&temp); switch(r) { case MP_LT: return 0; case MP_EQ: - if (convType == TCL_DD_STEELE0) { - return 0; - } else { - return isodd; - } + return isodd; case MP_GT: return 1; } @@ -3588,7 +3559,6 @@ ShouldBankerRoundUpToNext( static inline char * ShorteningBignumConversion( Double *dPtr, /* Original number being converted. */ - int convType, /* Conversion type. */ Tcl_WideUInt bw, /* Integer significand and exponent. */ int b2, /* Scale factor for the significand. */ int m2plus, int m2minus, /* Scale factors for 1/2 ulp in numerator. */ @@ -3609,7 +3579,6 @@ ShorteningBignumConversion( mp_int S; /* Denominator of the result. */ mp_int dig; /* Current digit of the result. */ int digit; /* Current digit of the result. */ - mp_int temp; /* Work area. */ int minit = 1; /* Fudge factor for when we misguess k. */ int i; int r1; @@ -3645,7 +3614,6 @@ ShorteningBignumConversion( mp_init_copy(&mplus, &mminus); mp_mul_2d(&mplus, m2plus-m2minus, &mplus); } - mp_init(&temp); /* * Loop through the digits. @@ -3666,8 +3634,7 @@ ShorteningBignumConversion( */ r1 = mp_cmp_mag(&b, (m2plus > m2minus)? &mplus : &mminus); - if (r1 == MP_LT || (r1 == MP_EQ - && convType != TCL_DD_STEELE0 && (dPtr->w.word1 & 1) == 0)) { + if (r1 == MP_LT || (r1 == MP_EQ && (dPtr->w.word1 & 1) == 0)) { mp_mul_2d(&b, 1, &b); if (ShouldBankerRoundUp(&b, &S, digit&1)) { ++digit; @@ -3686,8 +3653,8 @@ ShorteningBignumConversion( * commit to rounding up to the next higher digit? */ - if (ShouldBankerRoundUpToNext(&b, &mminus, &S, convType, - dPtr->w.word1 & 1, &temp)) { + if (ShouldBankerRoundUpToNext(&b, &mminus, &S, + dPtr->w.word1 & 1)) { ++digit; if (digit == 10) { *s++ = '9'; @@ -3774,7 +3741,7 @@ ShorteningBignumConversion( if (m2plus > m2minus) { mp_clear(&mplus); } - mp_clear_multi(&b, &mminus, &temp, &dig, &S, NULL); + mp_clear_multi(&b, &mminus, &dig, &S, NULL); *s = '\0'; *decpt = k; if (endPtr) { @@ -3804,7 +3771,6 @@ ShorteningBignumConversion( static inline char * StrictBignumConversion( Double *dPtr, /* Original number being converted. */ - int convType, /* Conversion type. */ Tcl_WideUInt bw, /* Integer significand and exponent. */ int b2, /* Scale factor for the significand. */ int s2, int s5, /* Scale factors for denominator. */ @@ -3822,7 +3788,6 @@ StrictBignumConversion( mp_int S; /* Denominator of the result. */ mp_int dig; /* Current digit of the result. */ int digit; /* Current digit of the result. */ - mp_int temp; /* Work area. */ int g; /* Size of the current digit ground. */ int i, j; @@ -3831,7 +3796,7 @@ StrictBignumConversion( * S = 2**s2 * 5*s5 */ - mp_init_multi(&temp, &dig, NULL); + mp_init_multi(&dig, NULL); TclInitBignumFromWideUInt(&b, bw); mp_mul_2d(&b, b2, &b); mp_init_set_int(&S, 1); @@ -3938,7 +3903,7 @@ StrictBignumConversion( * string. */ - mp_clear_multi(&b, &S, &temp, &dig, NULL); + mp_clear_multi(&b, &S, &dig, NULL); *s = '\0'; *decpt = k; if (endPtr) { @@ -3974,15 +3939,6 @@ StrictBignumConversion( * For floating point numbers that are exactly between two * decimal numbers, it resolves using the 'round to even' rule. * With this value, the 'ndigits' parameter is ignored. - * TCL_DD_STEELE - This value is not recommended and may be removed in - * the future. It follows the conversion algorithm outlined in - * "How to Print Floating-Point Numbers Accurately" by Guy - * L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, - * pp. 112-126]. This rule has the effect of rendering 1e23 as - * 9.9999999999999999e22 - which is a 'better' approximation in - * the sense that it will reconvert correctly even if a - * subsequent input conversion is 'round up' or 'round down' - * rather than 'round to nearest', but is surprising otherwise. * TCL_DD_E_FORMAT - This value is used to prepare numbers for %e format * conversion. It constructs a string of at most 'ndigits' digits, * choosing the one that is closest to the given number (and @@ -4032,10 +3988,6 @@ TclDoubleDigits( * one character beyond the end of the * returned string. */ { - int convType = (flags & TCL_DD_CONVERSION_TYPE_MASK); - /* Type of conversion being performed: - * TCL_DD_SHORTEST0, TCL_DD_STEELE0, - * TCL_DD_E_FORMAT, or TCL_DD_F_FORMAT. */ Double d; /* Union for deconstructing doubles. */ Tcl_WideUInt bw; /* Integer significand. */ int be; /* Power of 2 by which b must be multiplied */ @@ -4103,18 +4055,18 @@ TclDoubleDigits( * Correct an incorrect caller-supplied 'ndigits'. Also determine: * i = The maximum number of decimal digits that will be returned in the * formatted string. This is k + 1 + ndigits for F format, 18 for - * shortest and Steele, and ndigits for E format. + * shortest, and ndigits for E format. * ilim = The number of significant digits to convert if k has been - * guessed correctly. This is -1 for shortest and Steele (which + * guessed correctly. This is -1 for shortest (which * stop when all significance has been lost), 'ndigits' for E * format, and 'k + 1 + ndigits' for F format. * ilim1 = The minimum number of significant digits to convert if k has - * been guessed 1 too high. This, too, is -1 for shortest and - * Steele, and 'ndigits' for E format, but it's 'ndigits-1' for F + * been guessed 1 too high. This, too, is -1 for shortest, + * and 'ndigits' for E format, but it's 'ndigits-1' for F * format. */ - SetPrecisionLimits(convType, k, &ndigits, &i, &ilim, &ilim1); + SetPrecisionLimits(flags, k, &ndigits, &i, &ilim, &ilim1); /* * Try to do low-precision conversion in floating point rather than @@ -4187,7 +4139,7 @@ TclDoubleDigits( * [1.0e-3 .. 1.0e+24]). */ - return ShorteningInt64Conversion(&d, convType, bw, b2, b5, m2plus, + return ShorteningInt64Conversion(&d, bw, b2, b5, m2plus, m2minus, m5, s2, s5, k, len, ilim, ilim1, decpt, endPtr); } else if (s5 == 0) { /* @@ -4206,7 +4158,7 @@ TclDoubleDigits( m2minus += delta; s2 += delta; } - return ShorteningBignumConversionPowD(&d, convType, bw, b2, b5, + return ShorteningBignumConversionPowD(&d, bw, b2, b5, m2plus, m2minus, m5, s2/DIGIT_BIT, k, len, ilim, ilim1, decpt, endPtr); } else { @@ -4215,7 +4167,7 @@ TclDoubleDigits( * arithmetic for the conversion. */ - return ShorteningBignumConversion(&d, convType, bw, b2, m2plus, + return ShorteningBignumConversion(&d, bw, b2, m2plus, m2minus, s2, s5, k, len, ilim, ilim1, decpt, endPtr); } } else { @@ -4243,7 +4195,7 @@ TclDoubleDigits( * operations. */ - return StrictInt64Conversion(&d, convType, bw, b2, b5, s2, s5, k, + return StrictInt64Conversion(&d, bw, b2, b5, s2, s5, k, len, ilim, ilim1, decpt, endPtr); } else if (s5 == 0) { /* @@ -4260,7 +4212,7 @@ TclDoubleDigits( b2 += delta; s2 += delta; } - return StrictBignumConversionPowD(&d, convType, bw, b2, b5, + return StrictBignumConversionPowD(&d, bw, b2, b5, s2/DIGIT_BIT, k, len, ilim, ilim1, decpt, endPtr); } else { /* @@ -4270,7 +4222,7 @@ TclDoubleDigits( * fewer mp_int divisions. */ - return StrictBignumConversion(&d, convType, bw, b2, s2, s5, k, + return StrictBignumConversion(&d, bw, b2, s2, s5, k, len, ilim, ilim1, decpt, endPtr); } } diff --git a/generic/tclTest.c b/generic/tclTest.c index 4ed4f6a..4953133 100644 --- a/generic/tclTest.c +++ b/generic/tclTest.c @@ -1702,7 +1702,7 @@ TestdelassocdataCmd( * Parameters: * fpval - Floating-point value to format. * ndigits - Digit count to request from Tcl_DoubleDigits - * type - One of 'shortest', 'Steele', 'e', 'f' + * type - One of 'shortest', 'e', 'f' * shorten - Indicates that the 'shorten' flag should be passed in. * *----------------------------------------------------------------------------- @@ -1720,14 +1720,12 @@ TestdoubledigitsObjCmd(void *unused, { static const char* options[] = { "shortest", - "Steele", "e", "f", NULL }; static const int types[] = { TCL_DD_SHORTEST, - TCL_DD_STEELE, TCL_DD_E_FORMAT, TCL_DD_F_FORMAT }; diff --git a/generic/tclUtf.c b/generic/tclUtf.c index 4d9edf1..0fdd8a5 100644 --- a/generic/tclUtf.c +++ b/generic/tclUtf.c @@ -1966,7 +1966,7 @@ Tcl_UniCharCaseMatch( if ((p != '[') && (p != '?') && (p != '\\')) { if (nocase) { while (*uniStr && (p != *uniStr) - && (p != Tcl_UniCharToLower(*uniStr))) { + && (p != (Tcl_UniChar)Tcl_UniCharToLower(*uniStr))) { uniStr++; } } else { @@ -2158,7 +2158,7 @@ TclUniCharMatch( if ((p != '[') && (p != '?') && (p != '\\')) { if (nocase) { while ((string < stringEnd) && (p != *string) - && (p != Tcl_UniCharToLower(*string))) { + && (p != (Tcl_UniChar)Tcl_UniCharToLower(*string))) { string++; } } else { |
