【问题标题】:Program for float and doubles and how to print floats?浮点数和双打程序以及如何打印浮点数?
【发布时间】:2020-12-29 17:18:00
【问题描述】:

目前我是 C 的新手,我正在为一门课程做作业。任务是编写两个程序来计算tan(x)x 从 0 递增到 pi/2。一个程序必须使用浮点数,而另一个程序需要使用双精度数。作业的目标是了解输出的差异,因为它是科学编程课程的一部分。

到目前为止,我编写了这个程序:

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

int main()
{
    double x;
    
    double pi;
    pi = M_PI;
     
    for (x = 0; x <= pi / 2; x += pi / 20)
    {
        printf("x = %f, tan = %f\n", x, tan(x));
    }
    
    exit(0);
}

我得到反馈说输出是双倍的。昨天我开始了一个与这段代码相关的线程,但是由于出现了不同的问题,所以开始一个新的似乎是合适的。

循环的一般原理有效,但我不知道下一步该做什么。

让我们首先处理程序的双版本。由于我需要计算 tan(x)x 不是 100% 精确,由于小数增量,我认为将变量定义为双精度是很重要的。昨天得知printf函数的输出是双倍的。因此,我假设代码对于以 double 计算的目标是正确的。

在这里,我有一个小问题。我还打印了.20f,它给了我那个特定的窗口。我学到了一些关于 32 位和 64 位的知识。 Double 是 64 位的,可以在内存中存储 15 位小数。

问题:以上代码是否达到了双精度目标?

问题:如果内存已经超出,其他小数(第 16 位到第 20 位)从何而来?

对于float版本的程序,我需要将变量定义为float。

double x;
double pi;

变成

float x;
float pi;

然后是 printf 语句,在这里它变得有点混乱,因为我现在知道 printf 函数以双精度输出。但是我想要单精度并且仍然可以打印出来。我找不到另一个专门以单精度打印的函数。

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

int main()
{
    float x;

    float pi;
    pi = M_PI;

    for (x = 0; x <= pi / 2; x += pi / 20)
    {
        printf("x = %f, tan = %f\n", x, tan(x));
    }

    exit(0);
}

问题:如何以单精度打印?

问题:我需要在最后一个程序中进行哪些更改才能达到浮动程序的目标?

我渴望学习,但这有点难,因为由于 Covid-19,我们不再被允许进入大学。我希望有人能给我反馈。

谢谢, 叔

【问题讨论】:

  • 第 1 步是查看 IEEE 754 floating point format 并了解幕后情况。请记住,虽然它只能存储特定数量的二进制数字,这转换为十进制数字的粗略数量,但从二进制转换为十进制可能会在最后引入额外的数字作为转换过程的工件。例如,1/8th 是 0.125,即三位数字,尽管二进制中的 8 只有三位。
  • 无法printf以单精度打印,所有float参数将自动提升double .这是作为default argument promotion 的一部分完成的,所有variadic function 参数都会发生这种情况。
  • printf 本来就很好。但是您应该使用tanf 函数。 tanf 接受 float 参数,并返回 float 结果。
  • printf 的输出不是doubleprintf 的输出是一个字符序列,例如“1.347”。你说printf的输出是double是什么意思?你说的是它打印的位数吗?
  • 关于“问题:如果内存已经超出,其他小数(第 16 位到第 20 位)从哪里来?”:常见的 C 实现使用基于二进制的浮点格式。您看到double 可以存储“15 位小数”的陈述是错误的;它不存储任何小数位。它存储一个基于二进制的数字,本质上是一个与十进制数字几乎没有相关性的实数。当您打印超过 15 位数字时,您会在 double 中看到有关二进制数字实际值的更多信息。

标签: c floating-point scientific-computing


【解决方案1】:

printf 总是打印双精度,但这也意味着它可以打印浮点数,因为任何浮点数都可以转换为双精度而不损失精度。仅仅因为printf 打印一个双精度并不意味着它可以神奇地增加浮点精度。

double 是二进制的,所以说它可以存储 15 个十进制数字是没有意义的。正确的是,打印的前 15 位十进制数字是准确的。

【讨论】:

  • 谢谢,我只需要在我的浮动程序中将 tan 函数更改为 tanf 即可使其工作,这是真的吗?
  • @Terr070 我想是的。
【解决方案2】:

上面的代码是否达到了双精度目标?

没有。 "%f" 仅在小数点后打印 6 位,即使对于大值而言没有信息并且对于小值而言不足也是如此。要打印所有精度,请使用 "%a" - 但它不是十进制输出。

// printf("x = %f, tan = %f\n",x, tan(x));
printf("x = %a, tan = %a\n",x, tan(x));

要以十进制打印并具有足够的信息精度,请使用"%.*e""%.*g"Details.

printf("x = %.*e, tan = %.*e\n",DBL_DECIMAL_DIG-1,x, DBL_DECIMAL_DIG-1,tan(x));
printf("x = %.*g, tan = %.*g\n",DBL_DECIMAL_DIG  ,x, DBL_DECIMAL_DIG  ,tan(x));

如果内存已经超出,其他小数(第 16 位到第 20 位)从何而来?

这是十进制输出的产物,但浮点值是用二进制有效编码的。某些double 的精确打印需要数百位数字。 DBL_DIGDBL_DECIMAL_DIG 的有效数字经常很有用:通常是 15 到 17。

如何以单精度打印?
我需要在最后一个程序中进行哪些更改才能达到浮动程序的目标?

使用float 变量和函数并以指数表示法打印。使用FLT_DECIMAL_DIG 而不是DBL_DECIMAL_DIG

// printf("x = %f, tan = %f\n",x, tan(x));
printf("x = %.*e, tan = %.*e\n",FLT_DECIMAL_DIG-1,x, FLT_DECIMAL_DIG-1, tanf(x));

对于float,通常默认6个位置就可以了。

printf("x = %e, tan = %e\n", x, tanf(x));
printf("x = %g, tan = %g\n", x, tanf(x));

  double d = M_PI / 20;
  printf("x = %a, tan = %a\n", d, tan(d));
  printf("x = %.*e, tan = %.*e\n", DBL_DECIMAL_DIG - 1, d, DBL_DECIMAL_DIG - 1, tan(d));
  printf("x = %.*g, tan = %.*g\n", DBL_DECIMAL_DIG, d, DBL_DECIMAL_DIG, tan(d));

  float f = M_PI / 20;
  printf("x = %a, tan = %a\n", f, tan(f));
  printf("x = %.*e, tan = %.*e\n", FLT_DECIMAL_DIG - 1, f, FLT_DECIMAL_DIG - 1, tanf(f));
  printf("x = %e, tan = %e\n", f, tanf(f));
  printf("x = %g, tan = %g\n", f, tanf(f));

x = 0x1.41b2f769cf0ep-3, tan = 0x1.445f0fbb1cf91p-3
x = 1.5707963267948966e-01, tan = 1.5838444032453627e-01
x = 0.15707963267948966, tan = 0.15838444032453627
x = 0x1.41b2f8p-3, tan = 0x1.445f1p-3
x = 1.57079637e-01, tan = 1.58384442e-01
x = 1.570796e-01, tan = 1.583844e-01
x = 0.15708, tan = 0.158384

【讨论】:

  • 这真的很有帮助!谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-08-04
  • 1970-01-01
  • 2015-01-06
  • 1970-01-01
  • 1970-01-01
  • 2021-08-19
  • 1970-01-01
相关资源
最近更新 更多