为什么在声明浮点数时需要加上"f"?

103

示例:

float timeRemaining = 0.58f;

为什么在这个数字的末尾需要f


4
可能是因为否则它会被视为“double”。 - Uwe Keim
2
0.58(不带f后缀)是double类型的文字常量,不能将double值赋给float,就像不能将int值赋给string一样。但是您可以将float值分配给double,因为在这里您正在扩展(C#将隐式为您转换,因为不会丢失精度)。 - Dave New
1
可能是为什么应该在C#中使用文字常量?的重复问题。 - KV Prajapati
@AdamHouldsworth:我不确定这是否是链接问题的重复。在这个问题中,你需要使用f(否则会有编译器错误)。也许这个问题应该被编辑成“为什么编译器会给出这个错误?”——虽然几乎肯定有一个重复的问题。 - Jon
1
@AdamHouldsworth - 所有的路都通向 double & int - KV Prajapati
显示剩余3条评论
3个回答

114

你的浮点数声明包含两个部分:

  1. 它声明变量 timeRemaining 的类型为 float
  2. 它将值 0.58 赋给该变量。

问题出现在第二部分。

右侧表达式被单独计算。根据 C# 规范,一个包含小数点但没有后缀的数字会被解释为 double

因此,我们现在有一个 double 值,我们想要将其赋给一个类型为 float 的变量。为了做到这一点,必须存在从 doublefloat 的隐式转换。但是,由于在转换过程中可能会丢失信息(在本例中确实如此),因此不存在这样的转换。

原因是编译器使用的值实际上不是 0.58,而是最接近 0.58 的浮点值,对于 double 是 0.57999999999999978655962351581366...,对于 float 则是精确的 0.579999946057796478271484375。

严格来说,f并不是必需的。您可以通过将值转换为float来避免使用f后缀:

float timeRemaining = (float)0.58;

6
两个问题:你是如何得出数字'0.579999946057796478271484375'的?(float) 0.58会如何工作?你之前说过不进行转换,因为信息可能会丢失,那么为什么强制类型转换会起作用呢? - SexyBeast
11
  1. 我使用的是Windows计算器,它最多可以使用34位数字。
  2. 我说过没有隐式转换。强制类型转换是显式转换,因此您明确告诉编译器您需要进行转换,这是允许的。
- Jeffrey Sax
有趣...希望您仍在监视这篇文章。我之前在观看网络广播时接触到了后缀'm'。您如何让Windows计算器显示.58作为浮点值?我尝试了几个按钮,看起来它们会强制发生这种情况,但没有......一直得到0.58。我必须尊重一个如此熟练掌握工具的人... - user1585204
我理解 float 的整个 F 事情,但为什么呢?当你声明变量时,它被声明为浮点数,因此在编译时应该知道这个值是浮点数。当你声明一个 double 时也是同样的道理。这没有意义,因为你有 float myvalue = 2.4; 现在编译器应该已经知道 float 是变量的类型了。我错过了什么吗?谢谢! - Frank G.
@FrankG。两个原因:1.表达式“2.4”在其他地方被解释为双倍数。 2.不允许隐式缩小转换(例如从double到float)。如果您想对这些规则进行例外,那么您必须有一个非常好的理由。只是为了节省一个按键的操作可能不足以说服我们。 - Jeffrey Sax
显示剩余2条评论

39
由于编译器可以使用多种数值类型来表示值0.58:floatdoubledecimal。除非您允许编译器为您选择一个,否则您必须消除歧义。 double的文档说明如果您未指定类型,则编译器始终将double作为任何实数数字文字的类型:
引用: 默认情况下,在赋值运算符右侧的实数数字文字被视为double。但是,如果您希望将整数视为double,请使用后缀d或D。
添加后缀f创建一个float;后缀d创建一个double;后缀m创建一个decimal。所有这些后缀均可大写。
然而,这仍然不足以解释为什么这个代码无法编译:
float timeRemaining = 0.58;

答案的缺失部分是,将 double0.58 转换为 floattimeRemaining 可能会丢失信息,因此编译器拒绝隐式应用它。如果您添加显式转换,则会执行转换;如果您添加 f 后缀,则不需要转换。在这两种情况下,代码都将编译。

1
但是如果变量是浮点数,它怎么可能是双精度或十进制呢?我似乎漏掉了什么。 - Thomas
2
@BlazArt 是的,这句话说明编译器甚至不尝试检查赋值目标。这个原因将被埋藏在编译器团队所做的设计选择中。我猜最好明确表达以避免可能的混淆,或者说实现起来太昂贵了,而只是有两个规则,一个用于“int”,一个用于“double”。 - Adam Houldsworth
2
关于信息丢失,您能告诉我强制类型转换是如何实际进行的,与它们存储在IEEE 754格式中有关吗?Double具有更高的精度,这是否意味着从float到double的转换将始终起作用,例如double a = 0.69f; - SexyBeast
为什么CS不根据变量类型自动执行,就像其他语言一样呢? - mercury
如果你写 var x = 0,那么它将被解释为整数,如果 x=0.0 或者 0d,那么它会被解释为双精度浮点数,如果是 0.0f 或者 0f,那么就是单精度浮点数。 - Ferazhu
显示剩余2条评论

4
问题在于,为了允许涉及float和double的某些类型的隐式操作,.NET需要明确指定涉及混合操作数的所有情况下应该发生什么,或者允许在一个方向上执行类型之间的隐式转换;Microsoft选择效仿Java,允许偶尔有精度优势的方向,但经常牺牲正确性并且通常会产生麻烦。
在几乎所有情况下,取最接近特定数字数量的double值并将其分配给float,将产生最接近相同数量的float值。有一些极端情况,例如值9007199791611905;最好的float表示将是9007200328482816(误差为536870911),但将最佳的double表示(即9007199791611904)强制转换为float将产生9007199254740992(误差为536870913)。然而,将某个数量的最佳double表示转换为float通常会产生最佳可能的float表示,或者两个本质上一样好的表示之一。
请注意,即使在极端情况下,这种理想的行为也适用;例如,数量10^308的最佳float表示与转换该数量的最佳double表示所达到的float表示匹配。同样,数量10^309的最佳float表示与转换该数量的最佳double表示所达到的float表示匹配。
不幸的是,不需要显式转换的方向上的转换很少有如此准确。将某个值的最佳float表示转换为double很少会产生非常接近该值的最佳double表示,而且在某些情况下,结果可能相差数百个数量级(例如,将10^40的最佳float表示转换为double将产生一个比10^300的最佳double表示更大的值)。
遗憾的是,转换规则就是它们所是的样子,因此在“安全”方向上转换值时必须使用愚蠢的类型转换和后缀,并小心危险方向上的隐式类型转换,这些转换通常会产生虚假的结果。

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