【问题标题】:C math lib - float miscalculations?C 数学库 - 浮点计算错误?
【发布时间】:2016-10-12 08:26:01
【问题描述】:

我知道 C 中的浮点数不是很精确。但是我有一个片段,我不明白为什么会得到这样的结果。 首先,如果我在一行内进行计算,我会得到一个错误的“0”作为结果。 如果我将相同的计算拆分为几个命令,我得到的结果几乎是我所期望的。

所以第一个问题:

  1. 为什么单行与分割线不同?
  2. 有什么方法可以更精确地计算吗?我的意思是,320-(10.44*4) 应该是 278.24 而不是 278.23999!我理解像根或 pi 这样的数字的舍入问题。但不是在上面的例子中。

好的,这是我的代码(最小化的片段):

#include <math.h>
#include <stdio.h>

float kurv_steil,
        kurv_korr,
        t_abl_s,
        t_out,
        t_abl_s,
        b,
        t_tank_s;

void main()
{
    t_tank_s=32;
    kurv_steil=0.85;
    kurv_korr=-5;
    t_out=1.44;
    t_abl_s=24;

    printf("Werte: k_steil: %f k_korr: %f t_out: %f t_abl_s: %f\n",kurv_steil,kurv_korr,t_out,t_abl_s);
    b= pow(t_abl_s,(t_out/(320-(t_out*4) )))*0.65*kurv_steil*b*(-t_out+20);

    printf("[temp] Ganze Rechnung tank_s: %f\n",b);
    b=320-(t_out*4);

    printf("[temp] 320-(t_out*4) b: %f\n",b);
    b=t_out/b;

    printf("[temp] t_out/b b: %f\n",b);
    b=pow(t_abl_s,b);

    printf("[temp] t_abl_s^b b: %f\n",b);
    b=0.65*kurv_steil*b;

    printf("[temp] 0.65*kurv_steil*b b: %f\n",b);
    b=b*(-t_out+20);

    printf("[temp] b*(-t_out+20) b: %f\n",b);
    b=2*b+t_abl_s+kurv_korr;

    printf("[temp] 2*b*(-t_abl_s) b: %f\n",b);
}

输出是:

Werte: k_steil: 0.850000 k_korr: -5.000000 t_out: 1.440000 t_abl_s: 24.000000
[temp] Ganze Rechnung tank_s: 0.000000
[temp] 320-(t_out*4) b: 314.239990
[temp] t_out/b b: 0.004582
[temp] t_abl_s^b b: 1.014670
[temp] 0.65*kurv_steil*b b: 0.560605
[temp] b*(-t_out+20) b: 10.404831
[temp] 2*b*(-t_abl_s) b: 39.809662

我希望第一个输出具有相同的值“39.809662”,但它始终为“0”。为什么?

【问题讨论】:

  • 在您的代码发布之前缩进并用英文编写它,以便人们可以理解更多内容。
  • main 的签名可以是:int main (void)main(int argc, char *argv[])
  • 您可以做的一件简单的事情是更改打印数字的格式,而不是 %f 尝试使用 %.3f%6.3f。 C 中的数学非常准确,但结果并不总是以小数形式很好地显示。
  • 您说您了解浮点数不精确,然后您说 278.24 不是 278.23999。对于 32 位浮点数,这很好。您计算结果的顺序会强烈影响结果
  • 278.24 和 278.23999 的差值是 0.00001。假设您以米为单位测量场的长度,误差将是千分之一毫米,或 10 微米。或者,如果您的财富达到 278,240,000 美元,您会在意 10 美元吗?我不能称之为“不太精确”。

标签: c math floating-point


【解决方案1】:

在这一行

b= pow(t_abl_s,(t_out/(320-(t_out*4) )))*0.65*kurv_steil*  b  *(-t_out+20);

您正在使用右侧的变量 b。当程序启动时,这通常为零,因此乘以它的任何东西都将为零。不要依赖任何具有特定值的变量,始终初始化变量。

【讨论】:

  • 从什么时候开始通常为零?它是未定义的,我会假设它不比它更频繁地为零。可靠的答案,听起来很琐碎,却是一个严重的错误。
  • @KamiKaze 在这种情况下,变量是在文件范围内声明的,这意味着它具有静态存储持续时间,因此始终为零。由 C 标准保证,所以答案是正确的,除了“通常” - 它为零。尽管如此,编写依赖于静态存储持续时间变量的零初始化的程序仍然是一种不好的做法。
  • @Lundin 非常感谢您的澄清。
【解决方案2】:

如上所述,问题出在您使用的公式中。你有b=... *b*...b 此刻的值未知(很可能编译器将其设置为 0),所以你有 b=...*0*... 这使得最终结果具有 0 值。

您还应该将2* ... + t_abl_s+kurv_korr 添加到您的公式中

所以公式看起来像

b= 2 *
   pow(t_abl_s,(t_out/(320-(t_out*4) ))) *
   0.65 * kurv_steil *
   (-t_out+20) +
   t_abl_s + kurv_korr;

【讨论】:

    【解决方案3】:

    正如您在此链接中看到的,1.44 不能用二进制表示:

    http://www.wolframalpha.com/input/?i=1.44+in+binary

    所以你用它做的任何浮点计算都是不精确的。

    一个更熟悉的例子是尝试表示 1/3。在 base3 中,它只是 0.1。在十进制中,它是 0.33333……这与任何以 0.44 结尾的十进制数相同。不能用二进制精确表示。

    【讨论】:

      【解决方案4】:

      谢谢大家!

      天哪!我太傻了。是的,公式中确实有一个“*b”,导致始终为“0”,因为 b 之前没有初始化(显然随机总是为零)。

      关于计算的精度我不相信,因为 278.24 与 278.23999 不同。另一方面,它现在对我有用。

      谢谢!

      【讨论】:

      • 一旦你使用浮点数,你肯定会有错误。让他们保持低调是你的责任。因此,您不会将 float 与 == 进行比较。例如尝试 0.1+0.1+0.1 == 0.3 这应该是错误的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多