为什么整数/浮点数相乘会导致不同的结果?

9
如果我像下面这样乘以一个浮点数和整数,为什么所有的乘法都会产生不同的结果?我期望得到一致的结果。我认为在两种情况下,int值在乘法之前会被隐式转换为float。但是似乎存在差异。这种处理方式的原因是什么?
int multiply(float val, int multiplier)
{
    return val * multiplier;
}

int multiply2(float val, int multiplier)
{
    return float(val * multiplier);
}

float val = 1.3f;

int result0 = val * int(10); // 12
int result1 = 1.3f * int(10); // 13
int result3 = multiply(1.3f, 10); //12 
int result4 = multiply2(1.3f, 10); // 13

感谢您,Thorsten。

这是真正的代码吗?我得到了相同的结果,我不知道为什么会有所不同。 - Joseph Mansfield
2
第二个表达式可以在编译时求值 - 你使用的是哪个编译器?看一下它生成的字节码。 - Floris
在Ubuntu上尝试了g++ 4.7.3:两次乘法都导致13。 - gturri
编译器是 Visual Studio 2010 内置的。而且,它是真正的代码 =) - Thorsten
1
@Floris:你是正确的。反汇编显示,对于result1没有执行乘法操作。只有编译时常量值被使用。 - Thorsten
显示剩余2条评论
1个回答

10

你可能会遇到以下情况:

假设使用 IEEE 或类似的浮点数,1.3 无法表示,很可能是类似于 1.299999 的数字,乘以 10 后为 12.99999,然后被截断为整数 12。

但是,在编译时计算 1.3 * 10 可能会导致准确表示为 13。

根据代码实际结构、使用的编译器以及使用的设置,它可能在运行时或编译时计算任何一个表达式,从而将其评估为12或13。

为了完整起见,我可以使用下面的代码复现这个问题:

extern int result0;
extern int result1;

float val = 1.3f;

void foo( )
{   
 result0 = val * int(10); // 12
 result1 = 1.3f * int(10); // 13
}

编译器是否允许在编译时计算时使用不同的表示形式? - Joseph Mansfield
@sftrabbit 我的猜测是这是实现定义的。 - Tim Seguine
@sftrabbit:我手头没有标准的引用,但据我所知是这样的。这也涉及到模板不允许非类型浮点参数的原因所在。 - PlasmaHH
10
甚至允许使用不同的表示形式,这在动态情况下是允许的。根据§5/11:“浮点操作数的值以及浮点表达式的结果可以用比类型要求更高的精度和范围表示;但类型本身并未因此而改变”。这并非由实现定义,而是未指定的。(即,实现不必记录其操作,甚至可能连续两次执行不同的操作)。事实上,这会根据优化级别很容易发生变化。 - James Kanze
@JamesKanze 我认为这受到 ...::is_iec559FLT_EVAL_METHOD 的限制。如果编译器将前者定义为 true,后者定义为 0,则不应该执行任何此类操作。如果编译器将 FLT_EVAL_METHOD 定义为 12,则应生成根据相应模型计算的程序。 - Pascal Cuoq
@PascalCuoq C++11引入了一些限制,需要使用C++11编译器并指定FLT_EVAL_METHOD的值。在我手头的两个编译器中,一个(来自VS 2012的VC++)尚不支持此功能,另一个(g++ 4.7.2)始终使用long double,值为2。(默认情况下。如果我指定了-fpmath=sse -march=c3-2,它将输出0。没有这些选项,我得到的效果与OP描述的相同,有了这些选项,两个值都是13。) - James Kanze

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接