对 "same" 类型转换的不同结果感到困惑,float to int

Puzzled by different result from "same" type cast, float to int

如果我先将一个值分配给一个变量的浮点计算,然后将其分配给一个带有隐式类型转换的无符号整数,我得到一个答案。但是,如果我将相同的计算直接分配给 unsigned int,再次使用隐式类型转换,我会得到不同的答案。

下面是我编译并运行演示的示例代码:

#include <iostream>



int
main( int argc, char** argv )
{
    float   payloadInTons = 6550.3;


    //  Above, payloadInTons is given a value.
    //  Below, two different ways are used to type cast that same value,
    //  but the results do not match.
    float tempVal = payloadInTons * 10.0;
    unsigned int right = tempVal;
    std::cout << "    right = " << right << std::endl;


    unsigned int rawPayloadN = payloadInTons * 10.0;
    std::cout << "    wrong = " << rawPayloadN << std::endl;


    return 0;
}

有谁知道为什么 "right" 是对的,而 "wrong" 是错的?

顺便说一句,如果重要的话,我在 Ubuntu 14.04 LTS 上使用 gcc 4.8.2。

您正在使用 double 文字。使用适当的 float 文字,一切都很好。

int
main( int argc, char** argv )
{
    float   payloadInTons = 6550.3f;
    float tempVal = payloadInTons * 10.0f;

    unsigned int right = tempVal;
    std::cout << "     right = " << right << std::endl;

    unsigned int rawPayloadN = payloadInTons * 10.0f;
    std::cout << "also right = " << rawPayloadN << std::endl;


    return 0;
}

输出:

     right = 65503
also right = 65503

接受答案后

这不是 doublefloat 的问题。这是二进制浮点数和转换为 int/unsigned 的问题。

典型的 float 使用 binary32 表示并没有给出像 6550.3 这样的值的精确表示。

float payloadInTons = 6550.3;
// payloadInTons has the exact value of `6550.2998046875`.

下面乘以 10.0,确保计算至少达到 double 精度,精确结果为 65502.998046875。然后将乘积转换回 floatdouble 值无法在 float 中准确表示,因此四舍五入为最佳 float,精确值为 65503.0。然后 tempVal 根据需要将 right 转换为 65503 的值。

float tempVal = payloadInTons * 10.0;
unsigned int right = tempVal;

乘以下面的 10.0,确保计算的精度至少为 double,结果与以前一样准确 65502.998046875。这一次,该值直接转换为 unsigned rawPayloadN,不需要的值为 65502。这是因为值被截断而不是四舍五入。

unsigned int rawPayloadN = payloadInTons * 10.0;

由于转换,第一个“有效”是 doublefloatunsigned。这涉及 2 次转换,通常 不好 [=7​​1=]。在这种情况下,2个错误是正确的。


解决方案

如果代码尝试 float payloadInTons = 6550.29931640625;(下一个最小的 float 数字),两个结果都会是 65502

将浮点值转换为某种整数类型的“正确”方法通常是对结果进行舍入,然后执行类型转换。

float tempVal = payloadInTons * 10.0;
unsigned int right = roundf(tempVal);

注意:整个问题因 FLT_EVAL_METHOD 的值而变得复杂。如果用户的值不为零,浮点计算的精度可能会高于预期。

printf("FLT_EVAL_METHOD %d\n", (int) FLT_EVAL_METHOD);