【问题标题】:Why the need for "F" and "L" suffixes at the end of long and float data types?为什么在 long 和 float 数据类型的末尾需要 \"F\" 和 \"L\" 后缀?
【发布时间】:2022-07-15 03:37:16
【问题描述】:

为什么在声明 long 或 float 时需要“F”和“L”后缀?根据documentation

如果整数文字以字母Ll结尾,则它是long类型;否则它的类型是int
如果浮点文字以字母Ff结尾,则其类型为float;否则它的类型是double

因此,显然,编译器默认将值视为 int 数据类型或 double 数据类型。这并不能完全为我解释事情。

我挖得更深一点,发现了一个discussion,其中用户描述从 64 位双精度数转换为 32 位浮点数会导致数据丢失,而设计人员不想做出假设。

我还有问题:

  1. 为什么编译器允许写byte myByte = 100;,并且编译器自动将100,如上所述的int,转换为字节,但编译器不允许long myLong = 3_000_000_000;?为什么它不会自动将 3_000_000_000 转换为多头,尽管它在多头范围内?

  2. 如上所述,在设计 Java 时,由于数据丢失,设计者不允许将 double 分配给 float。虽然这对于超出浮点数范围的值可能是正确的,但显然像 3.14 这样的值对于浮点数来说已经足够小了。那么,为什么编译器会抛出赋值float myFloat = 3.14;的错误呢?

    最终,我无法完全理解为什么需要后缀,以及围绕自动转换的规则(如果这是幕后发生的事情)等。

    我知道之前已经讨论过这个话题,但给出的答案只会引发更多问题,所以我决定创建一个新帖子。

【问题讨论】:

标签: java


【解决方案1】:

在回答您的具体问题时:

  1. long myLong = 3_000_000_000; 的问题是3_000_000_000 不是合法的int 文字,因为 3,000,000,000 不适合 4 个字节。您想要将其提升为 long 以初始化 myLong 这一事实是无关紧要的。 (是的,语言设计者可以设计语言,以便在此上下文中 3_000_000_000 可以被解析为 long,但他们没有这样做,可能是为了使语言更简单并避免在其他上下文中出现歧义。)
  2. 3.14 的问题不是范围问题,而是精度损失问题。特别是,虽然 3.14 以 10 进制表示形式终止,但它在二进制浮点数中没有有限表示形式。因此,从 double 转换为 float(以便初始化 myFloat)将涉及截断表示的重要非零位。 (但要明确一点:Java 认为从doublefloat 的每个缩小转换都是有损的,无论涉及的实际值如何。因此float myFloat = 3.0; 也会失败。但是,float myFloat = 3; 会成功,因为从@ 转换987654337@ 值到 float 被认为是扩大转换。)

    在这两种情况下,正确的做法是通过将适当的后缀附加到数字文字来向编译器准确指示您要执行的操作。

【讨论】:

  • 为什么100 可以分配给既没有后缀也没有强制转换的byte,而不是3_000_000_000 分配给long
  • @Ted 谢谢你的回答。我理解这些要点,这是有道理的。我认为完全理解事物的问题之一是不知道“幕后”发生了什么。当你创建一个整数文字时,是否有在幕后发生的赋值?
  • @JohnKugelman - 这受赋值上下文中的转换规则约束 (Java Language Specification Section 5.2)。具体来说,如果该值是一个编译时常量,“如果变量的类型是byteshortchar,并且常量表达式的值可以在变量的类型。”请注意,如果该值不是常量,则根本不允许缩小原始转换。
  • @kvnr - 当您创建一个整数文字时,您的代码中没有任何赋值,但编译器会解析该值并将其存储在其数据结构中的某处。这发生在解析过程的早期,在分析上下文之前,因此每个文字都需要是合法值,而不管它将如何使用。
  • @TedHopp 说得通。知道我可以去哪里了解这个解析过程以及如何存储价值的好资源吗?知道会很有趣。
【解决方案2】:

为什么编译器允许写 byte myByte = 100;,并且编译器会自动将 100(如上所述的 int)转换为字节,但编译器不允许 long myLong = 3_000_000_000;?

因为规范是这么说的。请注意,byte myByte = 100; 确实有效,是的,但那是特例, 在 Java 语言规范中明确提及;通常,100作为.java文件中的文字总是首先被解释为int,并且绝不默默地将自己转换为一个字节,除了在两种情况下,都在 JLS 中明确提到:转换在修改后的赋值中是“隐含的”:someByteArr += anyNumber; 始终有效并暗示转换(同样,为什么?因为规范如此说明),并且当声明一个变量:byte b = 100;,假设 int 文字实际上在字节范围内(-128 到 +127)。

JLS 没有明确规定在 long x = veryLargeLiteral; 中应用此类概念。那就是你的追求真正应该结束的地方.规范是这样说的。故事结局。

如果你想问这个问题:“当然,无论是谁添加了这个,或者更确切地说,没有将这个明确的案例添加到 JLS 中,都有他们的理由,这些理由比他们认为的更技术性和优点“在梦中看到它”或“因为他们掷了硬币”,然后我们得到一个纯粹的猜测(因为你必须问他们,所以可能是詹姆斯高斯林,关于他为什么在 25 年前做出决定):

因为为 javac 代码库实现要复杂得多。

现在,文字首先被视为 int,只有在这个过程的后期,如果代码的结构使得 JLS 表示不需要强制转换,它们才能被“向下转换”。而对于较长的场景,这是行不通的:一旦您尝试将 3_000_000_000 视为 int,您就已经输掉了游戏,因为它不适合,因此解析它的解析器需要创建某种奇怪的“薛定谔的猫”样式节点,它准确地表示 3_000_000_000,但是除非在允许 silently-treat-as-long 部分的显式场景中使用它,否则下游将变成解析错误。这当然是可能的,但稍微复杂一些。

大概同样的论点适用于为什么 25 年来 java 没有看到更新。它可能会在某个时间点得到它,但我怀疑它是否具有高优先级。

如上所述,在设计 Java 时,由于数据丢失,设计者不允许将 double 分配给 float。

这真的一点关系都没有。 int -> long 是有损的,但 double -> float 大部分不是(它是浮点数,每次你用它们做很多事情时你都会损失一点,但当你使用它们时,这有点融入合同,所以不应该阻止你)。

显然像 3.14 这样的值对于浮点数来说已经足够小了。

Long 和 int 很容易:Int 从大约 -20 亿增加到大约 +20 亿,而 long 走得更远。但是 float/double 不是这样的。它们代表大致相同的范围(这是巨大的,300 位以上的数字很好),但是他们的准确性下降当您远离 0 时,对于浮点数,它下降得更快。几乎每个数字, 可能包括 3.14,无法完美呈现通过 float 或 double,所以我们只是在争论多少错误是可以接受的。因此,Java 通常不会默默地将东西转换为浮点数,因为,嘿,你选择了 double,大概是有原因的,所以你需要明确地告诉编译器:“是的。我明白了,我希望你转换和我会接受潜在的损失,这就是我想要的”,因为一旦编译器开始猜测你的意思,那就是出色的很难找到错误的来源。 Java 有很多地方是这样设计的。与 javascript 或 PHP 等语言形成对比,在这些语言中,大量代码是合法的,即使它们很奇怪并且似乎没有意义,因为编译器只会尝试猜测您想要什么。

Java 比这要好得多——它画了一条线;一旦你的代码足够奇怪以至于 javac 知道你想要什么的几率下降到一个阈值以下,java 将主动拒绝然后在黑暗中对你的意思进行疯狂的刺探并且会直截了当地拒绝并要求你更清楚关于它。在 20 年的编码生涯中,我怎么强调它的用处都不为过:)

我知道之前已经讨论过这个话题,但给出的答案只会引发更多问题,所以我决定创建一个新帖子。

然而你又问了同样的问题,而不是比这提出的“更多问题”。你不应该问这些吗?

【讨论】:

    【解决方案3】:

    首先,我们需要了解 Java 中的声明是如何发生的。 Java 是一种静态类型的语言,一旦我们声明了一个变量,我们就不能再改变变量的数据类型。让我们看一个例子:

    1. 长 myLong = 3_000_000_000;

      整数类型(byte、short、int、long)默认为“int”。不同之处在于大小(字节<短<整数<长)。

      当我们声明一个变量时,我们对 java 说“myLong”变量的类型应该是 long(它是 int 但长度更长)。在我们试图与“3_000_000_000”(文字)相等之后,它是 int 但 int 的最大值是 3,147,483,647,所以它更大。这就是为什么我们应该在文字的末尾写上“L 或 l”。添加“l”后,现在,我们的文字很长,我们可以与声明的长“myLong”相等。 => 长 myLong = 3_000_000_000l;

      1. int myInt = 300L; =>(会出现错误)

      在这个例子中,我们的 literal(300L) 很长。正如我之前提到的,long 的大小比其他整数类型大。当我们从文字末尾删除“L”时,“300”将是 int。

      1. 这是 FLoat 和 Double 的另一个示例:

        浮动 myFloat = 5.5; (错误) 浮动 myFloat = 5.5F; (正确的版本)

      默认情况下,Float 和 Double 是“double”。区别在于,比浮点数大一倍。 myFloat 一开始是“float”,5.5 是 double 所以会出现我们无法均衡的错误。这就是为什么我们应该在 5.5 的末尾添加“F 或 f”。我们可以使用“D 或 d”来表示 double,但这取决于我们,没有必要,因为没有比 double 更大的浮点类型了。

      希望它很清楚:)

    【讨论】:

      猜你喜欢
      • 2017-03-13
      • 1970-01-01
      • 2014-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-17
      相关资源
      最近更新 更多