问题描述
我使用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
。 -
value
和value_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;
}