为什么Tcl中的round()函数返回整数类型?

问题描述 投票:0回答:1

Tcl 提供了多种数学函数,例如

round()
ceil()
floor()
:

https://tcl.tk/man/tcl/TclCmd/mathfunc.htm

我最近注意到关于

round()
行为的一个有趣的“异常”,我想知道是否有更深层次的原因:虽然
ceil()
floor()
返回浮点类型,
round()
返回浮点类型任意精度整数。这可以产生令人惊讶的结果。例如:

% expr {floor(1000.5)/3}
333.3333333333333
% expr {ceil(1000.5)/3}
333.6666666666667
% expr {round(1000.5)/3}
333

非有限值参数的行为也因显而易见的原因而不同:

% expr {floor(+inf)}
Inf
% expr {ceil(+inf)}
Inf
% expr {round(+inf)}
integer value too large to represent

Tcl 与 C 语言有着密切的联系,它提供了

math.h
中定义的类似函数:

float round (float num);
float ceil (float num);
float floor (float num);

但是它们都返回浮点类型,包括

round()
。似乎大多数语言(例如Python)都选择了这条道路。 Tcl 中的
round()
不同有什么原因吗?

PS:Tcl版本是8.6.4

tcl
1个回答
0
投票

很抱歉发布我自己问题的答案。但由于我同时对这个主题进行了相当深入的研究,我想分享结果,尽管我还没有找到这个问题的明确答案,但我认为这是人们所能得到的:

round()
功能是在Tcl 7.0中添加的,可以追溯到1993年。它的实现在
tclExpr.c
中出现为:

static int
ExprRoundFunc(clientData, interp, args, resultPtr)
    ClientData clientData;
    Tcl_Interp *interp;
    Tcl_Value *args;
    Tcl_Value *resultPtr;
{
    resultPtr->type = TCL_INT;
    if (args[0].type == TCL_INT) {
    resultPtr->intValue = args[0].intValue;
    } else {
    if (args[0].doubleValue < 0) {
        if (args[0].doubleValue <= (((double) (long) LONG_MIN) - 0.5)) {
        tooLarge:
        interp->result = "integer value too large to represent";
        Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW",
            interp->result, (char *) NULL);
        return TCL_ERROR;
        }
        resultPtr->intValue = (args[0].doubleValue - 0.5);
    } else {
        if (args[0].doubleValue >= (((double) LONG_MAX + 0.5))) {
        goto tooLarge;
        }
        resultPtr->intValue = (args[0].doubleValue + 0.5);
    }
    }
    return TCL_OK;
}

此初始版本返回本机整数类型。后来,通过添加任意精度整数(TIP237),这被更改为任意精度整数:

https://tcl.tk/cgi-bin/tct/tip/237

更改日志没有给出选择返回整数类型的原因。它简单地写着:

167. 4/3/93 Changes to expressions:
    ...
    - Expressions now support transcendental and other functions, e.g. sin,
      acos, hypot, ceil, and round.  Can add new math functions with
      Tcl_CreateMathFunc().

源代码也不包含描述原因的注释。就提交信息而言,这是该语言的发明者 John Ousterhout 本人的决定。

有趣的是,大多数数学函数(包括

ceil()
floor()
)只是转发到相应的 C 库实现,而
round()
则没有:

#ifndef TCL_NO_MATH
#include <math.h>
#endif
//...

static BuiltinFunc funcTable[] = {
#ifndef TCL_NO_MATH
    {"ceil", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) ceil},
    {"floor", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) floor},
// ...
#endif
    {"int", 1, {TCL_EITHER}, ExprIntFunc, 0},
    {"round", 1, {TCL_EITHER}, ExprRoundFunc, 0},
// ...
};

有一个宏

TCL_NO_MATH
可以排除 C 库数学函数。然而,这样做并没有禁用浮点支持。为了能够将浮点类型转换为整数,即使排除 C 库数学函数,也需要
round()
的离散实现。

答案的推测部分是这样的:

思考的重点可能是进行转换,而不是模仿 C 原型。 一方面,它可能看起来更方便

set y [expr {round($x)}]

而不是

set y [expr {int(round($x))}]

获取整数值。另一方面,

int()
round()
在源代码中是邻居。即使使用
TCL_NO_MATH
,它们也可能被实现为“将浮点转换为整数类型的两种不同方法”。

© www.soinside.com 2019 - 2024. All rights reserved.