在C中返回零和Inf值

问题描述

我使用C使用以下代码进行计算:

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

void main() {
    float x = 3.104924e-33;
    int i = 6000,j = 1089;
    float value,value_inv;

    value = sqrt(x / ((float)i * j));
    value_inv = 1. / value;

    printf("value = %e\n",value);
    printf("value_inv = %e\n",value_inv);
}

实际上,我们可以看到value = 2.18e-20。这没有超出C中float数据类型的边界。但是为什么计算机会给我

value = 0.000000e+00
value_inv = inf

有人知道它为什么会发生以及如何在不将数据类型更改为double的情况下解决此问题吗?

解决方法

OP的float显然不支持sub-normals。 C允许不支持。

有人知道它为什么会发生以及如何在不将数据类型更改为两倍的情况下解决此问题吗?

这可能是实现细节或由于编译器选项。无需更改为double,而是使用其他编译器或选项。查看有关次标准支持,用于中间计算的精度和优化级别的选项(有时这样的情况会出现短边变化)。


在使用C11处理FLT_TRUE_MIN的法线的机器上,最小的非零float小于最小的{em> normal 的FLT_MIN。非零float

#include<float.h>
float xx = x/((float)i*j);
printf("xx = %e %e %e\n",xx,FLT_MIN,FLT_TRUE_MIN);

输出

xx = 4.751943e-40 1.175494e-38 1.401298e-45

在OP的情况下,没有低于标准的支持,xx变为0.0f并导致不期望的输出。


使用double数学将处理较小的中间float值。

value = sqrt(x/(1.0*i*j));  // Form product with `double` math
value_inv = 1.0f/value;     // Here we can just use float math
printf("value = %e\n",value);
printf("value_inv = %e\n",value_inv);

输出

value = 2.179897e-20
value_inv = 4.587373e+19
,

在我的计算机(Ryzen 2700X,x86_64)上,结果是:

value = 2.179897e-020
value_inv = 4.587373e+019

您可以尝试使用1.f而不是1.,它实际上是两倍:

value_inv = 1.f/value;
,

显然,您的系统不支持<form> <label for="fname">Add Ticker:</label><br> <input type="text" id="fname" name="fname"><br> <input type="submit" value="submit" onclick="AddWatchlist();" /> </form>的更多数字。在我的系统上,输出为:

float
,

我自己得到了答案。 我应该将sqrt(x/((float)i*j))更改为sqrt((double)x/((double)i*j))。之后,我可以获得正确的结果:

value = 2.179897e-20
value_inv = 4.587373e+19
,

没有理由使用float代替double进行此类计算:

  • 3.104924e-33是一个double常量,在分配时会转换为float,这可能会导致精度损失
  • sqrt获得一个double参数并返回一个double值。隐式转换再次发生,可能会失去精度。
  • 1. / value使用类型double进行计算,因为1.具有这种类型。 value在除法之前进行转换,结果转换为float并存储到value_inv
  • valuevalue_inv在传递给double时隐式转换为printf

所有这些转换都可能导致精度损失甚至被截断为0.。相反,除非强烈要求使用double,否则应始终使用float

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

int main() {
    double x = 3.104924e-33;
    int i = 6000,j = 1089;
    double value,value_inv;

    value = sqrt(x / ((double)i * j));
    value_inv = 1. / value;

    printf("value = %e\n",value);
    printf("value_inv = %e\n",value_inv);
    return 0;
}

如果出于某些原因需要使用float,请小心避免不必要的转换:

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

int main() {
    float x = 3.104924e-33F;
    int i = 6000,j = 1089;
    float value,value_inv;

    value = sqrtf(x / ((float)i * j));
    value_inv = 1.F / value;

    printf("value = %e\n",value_inv);
    return 0;
}