【问题标题】:Best practices for float multiplication in C++ or C?C++ 或 C 中浮点乘法的最佳实践?
【发布时间】:2017-03-27 21:47:54
【问题描述】:

我需要执行 400 * 256.3 的简单乘法运算。结果是 102520。简单明了。但是在 C++(或 C)中实现这种乘法对我来说有点棘手和困惑。

我知道浮点数不像在计算机中那样表示。我写了代码来说明这种情况。输出也附上。

所以,如果我使用浮点类型变量进行乘法运算,我会遇到舍入误差。使用双类型变量可以避免这个问题。但是假设我在嵌入式系统上的资源非常有限,我必须尽可能地优化变量类型,如何使用浮点类型变量执行乘法而不容易出现舍入错误?

我知道计算机完成的浮点数学运算根本没有被破坏。但我很好奇执行浮点数学的最佳实践。 256.3 只是一个说明值。我不知道在运行时我会得到什么浮点值。但它肯定是一个浮点值。

int main()
{
    //perform 400 * 256.3
    //result should be 102520

    float floatResult = 0.00f;
    int intResult = 0;
    double doubleResult = 0.00;

    //float = int * float
    floatResult = 400 * 256.3f;
    printf("400 * 256.3f = (float)->%f\n", floatResult);

    //float = float * float
    floatResult = 400.00f * 256.3f;
    printf("400.00f * 256.3f = (float)->%f\n", floatResult);

    printf("\n");

    //int = int * float
    intResult = 400 * 256.3f;
    printf("400 * 256.3f = (int)->%d\n", intResult);

    //int = float * float;
    intResult = 400.00f * 256.3f;
    printf("400.00f * 256.3f = (int)->%d\n", intResult);

    printf("\n");

    //double = double * double
    doubleResult = 400.00 * 256.3;
    printf("400.00 * 256.3 = (double)->%f\n", doubleResult);

    //int = double * double;
    intResult = 400.00 * 256.3;
    printf("400.00 * 256.3 = (int)->%d\n", intResult);

    printf("\n");

    //double = int * double
    doubleResult = 400 * 256.3;
    printf("400 * 256.3 = (double)->%f\n", doubleResult);

    //int = int * double
    intResult = 400 * 256.3;
    printf("400 * 256.3 = (int)->%d\n", intResult);

    printf("\n");

    //will double give me rounding error?
    if (((400.00 * 256.3) - 102520) != 0) {
        printf("Double give me rounding error!\n");
    }

    //will float give me rounding error?
    if (((400.00f * 256.3f) - 102520) != 0) {
        printf("Float give me rounding error!\n");
    }

    return 0;
}

Output from the code above

【问题讨论】:

  • 答案很简单:你不能。舍入问题是“内置”在使用 IEEE 浮点格式的计算机上,这几乎是所有问题。
  • 值 256.3f 也在骗你,你无法避免。
  • 您可以使用其他数据类型,例如固定精度类型或有理/分数库。
  • @Olaf 我的意思是说 C++ 或 C。斜线是“或”的符号。我知道计算机中的浮点实现,并且我知道它没有损坏。我只是好奇是否有办法让我使用浮点变量类型并且不会暴露于舍入错误。

标签: c precision multiplication floating-accuracy


【解决方案1】:

显示问题的一个关键弱点是转换为int intResult。发布的问题是关于乘法比较,但代码只显示了围绕int 转换的问题。

如果代码需要将 FP 值转换为最近的整数,请使用rint()round()nearbyint()lround(),而不是整数赋值。

【讨论】:

    【解决方案2】:

    首先,了解double 类型与float 类型有所有相同的问题。两种类型都没有无限精度,因此两种类型都容易受到精度损失和其他问题的影响。

    至于你能做什么:会出现许多不同的问题,这取决于你在做什么,以及许多克服这些问题的技巧。关于这些技术已经写了很多很多话。我建议对“避免浮点错误”进行网络搜索。但基本点是:

    • 知道浮点结果永远不会精确
    • 不要尝试比较浮点数是否完全相等
    • 比较浮点数是否相等时,请使用适当的“epsilon”范围
    • 计算后,将最终值显式四舍五入到所需精度通常是合适的(尤其是在打印出来时)
    • 注意每一步都会导致精度损失增加的算法

    另见https://www.eskimo.com/~scs/cclass/handouts/sciprog.html

    【讨论】:

    • 非常感谢您的建议!我很感激! :)
    【解决方案3】:

    如果您有固定的小数位数(256.3 的情况下为 1)以及结果的有界范围,您可以使用整数乘法,并通过整数除法调整小数位数的移位:

    int result = (400 * 2563) / 10;
    

    舍入误差是浮点算术固有的,除了可以精确表示所有操作数的少数情况。无论您选择float 还是double 只会影响何时错误发生,而不是如果

    【讨论】:

    • 非常感谢您的建议!但是如果我直到运行时才知道操作数的值怎么办?
    • 您对浮点输入了解多少?他们的domain 是什么?为什么舍入错误是个问题?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-10
    • 1970-01-01
    • 2011-12-23
    • 2013-11-08
    • 1970-01-01
    相关资源
    最近更新 更多