【问题标题】:How do I get C to differentiate between single and double floats when evaluating trig functions?在评估三角函数时,如何让 C 区分单浮点数和双浮点数?
【发布时间】:2020-01-16 14:12:44
【问题描述】:

我对 C 编程非常陌生。对于单精度和双精度浮点数,我必须以 pi/20 的步长打印 tan(0) 到 tan(pi/2) 的值。但是,当我使用不同的数据类型来存储浮点数时,单数和双数之间没有任何变化,我预计位数会发生变化。

#include <stdio.h>
#include <math.h>
#define PI 3.1415926

int main()
{
    float angle = 0.0;
    float pi_single = 3.1415926;
    printf("single precision:\n");
    while(angle < pi_single/2){
            float(tanangle) = 0.0;
        tanangle = tan(angle);
        printf("tan(%f) = %f\n", angle, tanangle);
        angle = angle + pi_single/20;
    }

    double angle2 = 0.0;
    double pi_double = 3.141592653589793;
    printf("double precision:\n");
    while(angle2 < pi_double/2 ){
            double(tanangle2) = 0.0;
        tanangle2 = tan(angle2);
        printf("tan(%lf) = %lf\n", angle2, tanangle2);
        angle2 = angle2 + pi_double/20;
    }
    return 0;
}

我正在尝试复制这个 Python 程序的结果:

import numpy as np
theta_32 = np.arange(0, np.pi/2+np.pi/20, np.pi/20, dtype = 'float32')
print('single precision')
for theta in theta_32:
    print(theta)
    print(np.tan(theta))
print()

[enter image description here][1]
theta_64 = np.arange(0, np.pi/2+np.pi/20, np.pi/20, dtype = 'float64')
print('double precision')
for theta_new in theta_64:
    print(theta_new)
    print(np.tan(theta_new))

【问题讨论】:

  • float(tanangle) = 0.0; 是定义tanangle 的一种非常奇怪的方式。大部分人写float tanangle = 0.0;,用括号写会让人迷惑。你为什么这样写?
  • 对,对不起。我不习惯声明变量,这可能是 Python 遗留下来的习惯。

标签: c floating-point


【解决方案1】:

如果您希望在float 而不是double 中进行计算,则需要使用tanf

还要注意,使用浮点数时,最好使用整数类型int i,例如从 0 到 19 计数,然后使用 pi * i / 20 作为角度。如需进一步阅读,请参阅Is floating point math broken?

【讨论】:

  • Wikipedia 将单精度浮点数的格式说明符指定为 %f,将双精度浮点数指定为 %lf。长双精度为 %Lf。
  • @pythonraptor:哎呀,谢谢你,欢迎来到 Stack Overflow。
  • %f%lf 在 C99 及更高版本中用于打印 floatdouble(在 C90 中,标准仅定义了 %f)。将%Lf 用于long double。对于scanf()(未在问题中使用),格式为%f 对应float%lf 对应double%Lf 对应long double%f 可以同时打印floatdouble 的原因是默认参数提升规则意味着printf()float 参数会自动转换为`double。
  • 很抱歉,我尝试了您的建议,但仍然无法找到合适的解决方案。您能否提供更多指导或您自己的解决方案?
  • 即使使用 for 循环和 tanf vs tan,对于单变量和双变量,我仍然得到相同的结果。
【解决方案2】:

这里有两个主要问题:

首先,tan 函数接受 double 并返回 double。使用tanf 代替它接受float 并返回float。所以改变这个:

tanangle = tan(angle);

到这里:

tanangle = tanf(angle);

其次,%f 格式说明符默认打印 6 位精度。这还不足以看出单精度和双精度浮点之间的区别。将精度扩大到 15 位,您会看到差异。那么这个:

printf("tan(%f) = %f\n", angle, tanangle);

变成:

printf("tan(%.15f) = %.15f\n", angle, tanangle);

还有这个:

printf("tan(%lf) = %lf\n", angle2, tanangle2);

变成:

printf("tan(%.15lf) = %.15lf\n", angle2, tanangle2);

【讨论】:

  • 谢谢!我现在看到了不同!如果可以的话,还有两个问题;在 Python 中,当我存储单精度浮点数时,实际打印的数字比打印双精度浮点数时少。为什么C不一样?在评估 C 与 Python 中的角度时,我得到的结果也略有不同。此外,为了使最后一个角度以双精度接近 pi/2,我必须在 while 循环中使用 pi/2 + pi/20。为什么这不会以单精度发生?
【解决方案3】:

我预计位数会改变

printf() 控制的是打印的位数,而不是变量的类型。

使用"%e" 查看float 的浮点特性,而不是使用"%f" 固定。 要查看所有有用的数字,而不是打印到默认精度 6,请使用 precision 修饰符。

xxx_DECIMAL_DIG - 1 是与"%e" 一起使用的小数位数,以查看xxx_DECIMAL_DIG 有效数字的总数。它是区分所有浮点类型所需的位数。 ref

#include <float.h>

  // printf("tan(%f) = %f\n", angle, tanangle);
  printf("tan(%.*e) = %.*e\n", FLT_DECIMAL_DIG - 1, angle, DBL_DECIMAL_DIG - 1, tanangle);

  // printf("tan(%f) = %f\n", angle2, tanangle2);
  printf("tan(%.*e) = %.*e\n", DBL_DECIMAL_DIG - 1, angle2, DBL_DECIMAL_DIG - 1, tanangle2);

当结果保存在float 中时,代码可以使用atanf() 来获取float 结果。

// tanangle = tan(angle);`
tanangle = tanf(angle);`

printf("tan(%.*e) = %.*e\n", FLT_DECIMAL_DIG - 1, angle, FLT_DECIMAL_DIG - 1, tanangle);

而不是用固定值近似pi,让系统计算最好。

//float pi_single = 3.1415926;
//double pi_double = 3.141592653589793;
float pi_single = acosf(-1);
double pi_double = acos(-1);

最好使用整数循环

for (int i=0; i<= 20; i++) {
  float angle = i*pi_single/20;
  float tanangle = tanf(angle);
  printf("tan(%.*e) = %.*e\n", 
    FLT_DECIMAL_DIG - 1, angle, 
    FLT_DECIMAL_DIG - 1, tanangle);
}

【讨论】:

  • 完美,谢谢。是否可以用常规小数而不是科学计数法显示?
  • 不错的答案,但可以按照您建议的pi 的方式计算一项预订。问题在于,没有标准(甚至 IEEE754 也没有)保证返回适合您的类型的 pi 的最佳结果。比自己定义或使用数学库要好得多。我知道这是 C++ 但请参阅 stackoverflow.com/questions/49778240/…
  • @pythonraptor。 “是否可以用常规小数而不是科学计数法显示?” --> 当然,使用"*.f"。当x 很小(无信息的前导零、截断的数字)或x 很大(无信息的最低有效数字)时,这将提供更少的信息。尝试printf("%.*g\n", DBL_DECIMAL_DIG, some_double); 作为折衷方案。
  • @Bathsheba 应该 acos(-1) 不提供最好的机器 pi,我同意不能保证正如你评论的那样,数学库的质量很差,任何 trig math.h 结果都会有弱点/也怀疑。也许作为一种折衷,代码可以将 pi 常数指定为肯定多于 99.99999% 的编译器今天需要的位数,例如 double pi_d = ‭3.14159265358979323846264338327950288‬; pi_f = ‭3.14159265358979323846264338327950288‬f;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-04
  • 2015-10-20
  • 2010-12-19
  • 2020-12-29
  • 1970-01-01
  • 2017-08-19
  • 2011-10-24
相关资源
最近更新 更多