(浮动)(1.2345f * 6.7809)比1.2345f * 6.7809f更准确吗?

时间:2014-11-04 06:56:25

标签: c++

我有一些代码块:

float total = <some float>;
double some_dbl = <some double>;

total *= some_dbl;

这引发了我想要关闭的编译器警告,但我不想关闭此类警告 - 相反,我宁愿根据需要明确地转换类型。让我思考的是...... (float)(total * some_dbl)total * (float)some_dbl准确得多吗?它是编译器还是平台特定的?

更好的代码示例(链接如下):

#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;

int main() {
    double d_total = 1.2345678;
    float f_total = (float)d_total;
    double some_dbl = 6.7809123;

    double actual = (d_total * some_dbl);
    float no_cast = (float)(f_total * some_dbl);
    float with_cast = (float)(f_total * (float)some_dbl);

    cout << "actual:               " << setprecision(25) << actual << endl;
    cout << "no_cast:              " << setprecision(25) << no_cast << endl;
    cout << "with_cast:            " << setprecision(25) << with_cast << endl;
    cout << "no_cast, nextafter:   " << setprecision(25) << nextafter(no_cast, 500.0f) << endl;

    cout << endl;

    cout << "Diff no_cast:   " << setprecision(25) << actual - no_cast << endl;
    cout << "Diff with_cast: " << setprecision(25) << with_cast - actual << endl;
    return 0;
}

修改 所以,我给了他一个机会。通过我尝试的示例,我确实找到了一个total * (float)(some_dbl)似乎更多准确的地方。我认为这并不总是如此,但相反是抽奖的运气,或者编译器截断双精度来浮动,而不是舍入,导致可能更糟的结果。请参阅:http://ideone.com/sRXj1z

编辑2 :我确认使用std::nextafter (float)(total * some_dbl)返回截断值,并更新了链接代码。这是非常令人惊讶的:如果在这种情况下编译器总是截断双精度,那么你可以说(float)some_dbl <= some_dbl,这意味着with_cast <= no_cast。但是,这不是不是的情况! with_cast不仅大于no_cast,而且更接近实际值,这有点令人惊讶,因为我们在乘法发生之前丢弃信息。

4 个答案:

答案 0 :(得分:10)

根据所涉及数字的大小,它会有所不同,因为double不只是更精确,但也可以包含大于float的数字。这是一个示例,它将显示一个这样的实例:

double d = FLT_MAX * 2.0;
float f = 1.0f / FLT_MAX;

printf("%f\n", d * f);
printf("%f\n", (float)d * f);
printf("%f\n", (float)(d * f));

输出:

2.000000
inf
2.000000

这是因为虽然float显然可以保留计算的结果 - 2.0,但它不能保留FLT_MAX * 2.0的中间值

答案 1 :(得分:2)

如果执行操作,则编译器会将变量转换为该操作的最大数据类型。这是双倍的。在我看来,操作:(float)(var1f * var2)具有更高的准确性。

答案 2 :(得分:1)

我测试过它们并不相同。以下结果为truehttp://codepad.org/3GytxbFK

#include <iostream>

using namespace std;

int main(){
  double a = 1.0/7;
  float b = 6.0f;
  float c = 6.0f;
  b = b * (float)a;
  c = (float)((double)c * a);
  cout << (b-c != 0.0f) << endl;
  return 0;
}

这让我有理由:从乘法结果表示为doublefloat将有更好的回合机会。有些位可以通过float乘法结束,当double上执行乘法然后将其转换为float时,该乘法会正确计算。

BTW,我选择1/7 * 6,因为它以二进制重复。

编辑:经过研究,似乎对于从double到float的转换以及浮点数的乘法,舍入应该相同,至少在符合IEEE 754的实现中是这样。{{3} }

答案 3 :(得分:1)

根据代码转储中的数字,float的两个相邻可能值为:

        d1 =  8.37149524...
        d2 =  8.37149620...

以双精度进行乘法的结果是:

              8.37149598...
当然,

介于这两者之间。将此结果转换为float 实现定义是否&#34;轮次&#34;上或下。在您的代码结果中,转换选择了d1,这是允许的,即使它不是最接近的。混合精度乘法以d2结束。

因此,我们可以得出结论,有点不直观的是,以双精度计算双精度然后转换为float在某些情况下不如完全以float精度完成!