在C语言中,float的行为是怎样的?

3
#include<stdio.h>
int main()
{
   float f = 0.1;
   double d = 0.1;
   printf("%lu %lu %lu %lu\n", sizeof(f), sizeof(0.1f), sizeof(0.1), sizeof(d));
   return 0;
}

输出

$ ./a.out 
4 4 8 8

根据上述代码,我们可以看到sizeof(0.1)sizeof(0.1f)不同。其中sizeof(0.1)为8个字节,而sizeof(0.1f)为4个字节。但是在将值赋给浮点变量f时,它会自动截断其大小为4个字节
在下面的代码中,与浮点数x进行比较时,它不会截断,并且将4个字节的float8个字节的0.1进行比较,float x的值与0.1f相匹配,因为两者都是4个字节
#include<stdio.h>
int main()
{
    float x = 0.1;
    if (x == 0.1)
        printf("IF");
    else if (x == 0.1f)
        printf("ELSE IF");
    else
        printf("ELSE");
}

输出

$ ./a.out 
ELSE IF

为什么在赋值时截断,而在比较时不截断?如何解决这个问题?


2
float类型是单精度浮点类型,而double双精度浮点类型。我还建议您阅读"Is floating-point math broken?",这将帮助您理解为什么0.1f != 0.1 - Some programmer dude
有没有什么方法来解决四舍五入的问题? - Nitin Tripathi
1
@NitinTripathi 不要将浮点数进行相等比较,并期望它们总是会有一定的误差。除非绝对必要,否则也可以避免使用 float 类型。 - fuz
不使用浮点数将会强制我们使用双精度,这将增加另外4个字节的开销,这也不是良好的编程实践 :( - Nitin Tripathi
5个回答

7

没有后缀的浮点数字面值是double类型。加上后缀f将产生float类型字面值。

在赋值时,等号右操作数会被转换为左操作数的类型,因此您会观察到截断。

在比较时,==运算符的操作数会转换为两个操作数中较大的一个,所以x == 0.1就像(double)x == 0.1,这是错误的,因为由于舍入问题,(double)(float)0.1不等于0.1。在x == 0.1f中,两个操作数都具有float类型,这将在您的计算机上导致相等。

浮点数学是棘手的,请阅读标准获取更多详细信息。


谢谢@FUZxxl,那真的很有帮助。 无论你在第三段说什么,我都试图实现它并发现以下内容: printf("%lu %lu %lu %lu\n", sizeof((double)f), sizeof((double)0.1f), sizeof((double)0.1), sizeof((double)d)); printf("%15.15f %15.15f %15.15f %15.15f\n", (double)f, (double)0.1f, (double)0.1, (double)d);输出: $ ./a.out 4 4 8 8 8 8 8 8 0.100000001490116 0.100000001490116 0.100000000000000 0.100000000000000如果我们看到,所有四个类型转换后转换为8字节,但从float到double时会出现舍入问题。 - Nitin Tripathi
1
@NitinTripathi,你的评论有问题,sizeof((double)x)无论x是什么,输出的结果都将始终为8 - M.M
感谢@M.M指出。更正如下:printf("%lu %lu %lu %lu\n", sizeof((double)f), sizeof((double)0.1f), sizeof((double)0.1), sizeof((double)d)); printf("%15.15f %15.15f %15.15f %15.15f\n", (double)f, (double)0.1f, (double)0.1, (double)d); 输出:$ ./a.out 8 8 8 8 0.100000001490116 0.100000001490116 0.100000000000000 0.100000000000000 如果我们看到,所有四个都通过类型转换转换为8字节,但在从float到double的类型转换时会出现舍入问题。 - Nitin Tripathi

2

浮点常量如0.1,如果没有指定为0.1f,则默认为双精度。下面这行代码:

float f = 0.1;

意思是创建一个值为0.1的双精度数,并将其转换为单精度数,在此过程中会失去精度。而下面这几行代码:

float x = 0.1;
if (x == 0.1)

这将导致x被隐式转换为双精度浮点型,但其值与例如double x = 0.1;略有不同。


1

0.1f(数字后面的“f”)是表示计算机中的浮点数,这样编译器就知道需要将其存储为浮点数而不是双精度数。 因此,浮点数0.1不等于0.1,它等于0.1f。


1
当你写 0.1 时,默认情况下它被视为 double 类型。后缀 f 显式地将其转换为浮点型。
在第二个问题中,浮点数以 IEEE 标准 存储,因此它进入了 else if 分支,因为将 0.1f 转换为 double 的等效转换不同。

https://en.wikipedia.org/wiki/Floating_point


1

0.1 是一个双精度值,而 0.1f 是一个浮点值。 我们之所以可以写 float x=0.1double x=0.1 是因为存在隐式转换。

但是通过使用后缀 f,你可以将其变成浮点型。

在这个例子中 -

if(x == 0.1)

这是错误的,因为在小数点后面的某些位置上,0.1并不完全等于0.1。还进行了向更高类型(即double)的转换。

将其转换为float,然后再转换为double,会丢失信息,因为doublefloat具有更高的精度,所以它们之间存在差异。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接