将变量转换为`float`和在初始化`float`时添加`f`后缀有什么区别?

65

什么是区别?

float f = (float) 99.32 ;
float f = 99.32f ;

它们俩都编译并运行成功了。


15
第一个存在“双重舍入误差”(double rounding error),这不好,因为结果可能不是所期望的值。第二个则产生正确舍入的浮点值。 - phuclv
该链接使用了太长的示例数字表示方式。 "0.50000008940696713" 已足够。 - Random832
@LưuVĩnhPhúc:我认为它使用了一个精确的十进制表示法来表示两个相邻双精度值之间的中间值,但我建议将一个大整数添加到该值中,这样可以使精确的十进制表示更短且更明显。 - supercat
  1. 你的意思是针对那个十进制字符串,还是任何一个?
  2. “成功”是什么意思?“返回相同的值”?(它们是否测试为相等?您是否检查了整个表示形式?)
- philipxy
6个回答

88
float f = 99.32f ;

那是一个 float 字面值,意味着将一个 float 变量直接赋值为一个 float 值。

float f = (float) 99.32 ;

这是一个float变量,它被赋予一个double值,在赋值之前将其强制转换为float


6
编译器会对这个进行优化吗?(在强制类型转换的情况下) - Austi01101110
16
不,99.32 是一个浮点型字面量,它的类型是 double - mch
22
不,99.32是一个double类型的浮点数,而不是被隐式转换为它。这被称为“double类型的浮点字面量”。99.32f是一个float类型的浮点数,也没有进行转换。这被称为“float类型的浮点字面量”。在float f = 99.32f;double d = 99.32;中没有进行转换。 - mch
8
这两者之间的价值有何不同? - John Dvorak
6
罕见的情况下才会出现这种情况,即(float)99.32是实际数字99.32四舍五入到最接近的double数字,再四舍五入到最接近的float数字。99.32f是实际数字99.32四舍五入到最接近的float数字。假设有一个数x正好处于两个相邻的float数字之间,d和d'是比x略大/略小的两个十进制数字。此时,使用.f后缀将进行上/下舍入,但四舍五入为double可能会在两种情况下都得到结果x,并且强制转换为float将在错误的方向上将d或d'四舍五入。 - gnasher729
显示剩余17条评论

25

虽然这两种情况可能被优化掉,但是在第一种情况下,您有一个双精度字面量,它被类型转换为浮点型,而在第二种情况下,您有一个浮点字面量。

如果没有被优化掉,你会在第二个例子中的代码中得到一个类型转换。

但是,在某些边角情况下,结果可能略有不同(取决于四舍五入模式)。如果您的数字无法精确表示,那么在第一种情况下,您将获得两次舍入-首先将十进制表示舍入为双精度,然后将其舍入为浮点数,而在第二种情况下,您直接将十进制表示舍入为浮点数。


由于单精度浮点数中的ULP比双精度浮点数中的ULP要大得多,因此我很难相信加倍舍入会产生显着影响。 - Kevin
1
@Kevin 它将会有高达一个_float_ ULP 差异的影响。 - John Dvorak
1
我怀疑存在一个边角情况,但还没有找到它。 - chux - Reinstate Monica
11
0.50000008940696713会得到不同的结果:使用四舍五入你会得到0x1.000004p-1,而使用后缀则会得到0x1.000002p-1(参见http://ideone.com/EV3QeG)。另请参见https://en.wikipedia.org/wiki/Rounding#Double_rounding。 - Matthieu M.
2
更具体地说,11-21的最后两位数字均为双精度浮点数的0x1.000003p-1(因此当双精度浮点数舍入为单精度浮点数时为0x1.000004p-1),而11-16(大约是该范围的一半)的最后两位数字为0x1.000002p-1。因此,在每个实例中可能发生这种情况的值范围(可能相隔一个浮点ULP)大约为一个双精度ULP的一半宽度,并且在每种情况下向上或向下舍入时会产生一个半浮点ULP的“误差”,因为实际值大约处于两个浮点值之间的中间位置。 - Random832
显示剩余2条评论

11
在第一种情况下,没有进行类型转换,99.32 被解释为 double 而不是 float。
一个 double 字面量被强制转换为 float。
在第二种情况下,使用后缀 f 确保编译器将 99.32 解释为 float。

11
在代码 "float f = (float) 99.32;" 中,字面值 "99.32" 默认创建为 double 类型,然后强制转换为 float 类型。
在代码 "float f = 99.32f ;" 中,由于 "99.32f" 中的结尾字母 f,字面值默认创建为 float 类型,不需要类型转换。
后者类似于写成 "double f = 99.32;",因为你会直接将匹配类型的变量赋值为 double 类型。

10

没有后缀的浮点数字面值默认为 double 类型。

因此,声明 float f = (float) 99.32; 你首先显式地将类型为 double 的数字面值 99.32 转换为 float,然后将其赋值给变量 f

声明 float f = 99.32; 同样做了相同的事情,但在这种情况下,类型转换是隐式进行的

如果您想避免隐式转换,应使用 f 后缀来定义您的数字面值(即,float f = 99.32f;


不,它们不是同一件事。如果没有 f 后缀,结果有时会出错。 - phuclv

6

区别在于字面量99.32是double类型,而字面量99.32f是float类型。

第一条语句将float字面量分配给一个float变量。没什么特别的。

第二条语句将double字面量强制转换为float,并将结果分配给float变量。

就标准而言,您可以将double字面量分配给float变量,而不需要显式地进行强制转换。在这种情况下会发生隐式转换。例如:

float f = 99.32;

你甚至可以做到:

float f = (double) 10.5f;

右边仍然会被隐式转换为浮点数。

请注意,大多数现代编译器会优化掉这些内容,因此表示浮点数通常是风格和偏好的问题。 只要保持一致即可。


这是错误的,它们不仅仅是为了一致性,而是为了准确性。 没有 f 后缀,结果有时会出错。 始终使用 f 表示 float,并使用 L 表示 long double. - phuclv

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