【问题标题】:C float assignment from literal来自文字的 C 浮点赋值
【发布时间】:2014-10-02 07:00:45
【问题描述】:

在阅读book about 3D graphics 的一部分时,我遇到了以下作业:

const float vertexPositions[] = {
0.75f, 0.75f, 0.0f, 1.0f,
0.75f, -0.75f, 0.0f, 1.0f,
-0.75f, -0.75f, 0.0f, 1.0f,
};

为什么需要 f 后缀?文字的类型不能从变量的类型中确定吗?我相信没有 f 浮点文字被解释为双精度数,但是为什么当数组显然是浮点类型时呢?

【问题讨论】:

  • 我相信这是因为代码是如何编译的,编译器在知道数组类型之前就知道了字面量。
  • 使用 GCC 而没有 f 我没有收到任何错误。
  • 最好还是添加f
  • 在某些编译器上,ASSERT(1.1 != 1.1f);
  • @franji1 大多数编译器都这样。您只需要 float 成为 IEEE 754 binary32 和 double 成为 binary64,因为它们几乎无处不在。 blog.frama-c.com/index.php?post/2011/11/08/Floating-point-quiz

标签: c floating-point assign


【解决方案1】:

虽然省略f 可能不会出错,但输入它是一种很好的做法,原因如下:

float num = 0.1;
(num == 0.1)

评估将产生0,因为num 作为浮点数舍入为0.1,而表达式中的0.1 是双精度数。所以它们实际上并不相同,尽管它们可能看起来是一样的。

【讨论】:

  • 请注意,在下面的评估中,num 的提升是加倍的,因为您正在与双倍文字进行比较
  • @pqnet 不正确。在发表评论之前先尝试一下。
  • @Igor 我可以确认(num == 0.1) 中的num 被提升为double。此外,表达式(num == 0.1f) 的计算结果将如预期的那样为1float num = 0.1; (num == 0.1) 的问题完全在于比较,float num = 0.1f; (num == 0.1) 会产生完全相同的结果。
  • 它产生一个零,我发誓。 @PascalCuoq
  • @Igor 当然。参与这里讨论的每个人都知道(num == 0.1) 产生0。 pqnet 和我都没有为此争论。
【解决方案2】:

不,永远不会推断文字的类型。如果没有f,它们就是double 文字,并且指令是一个可能会丢失精度的转换(在这种情况下并非如此,因为所有文字也都用浮点数精确表示)。

如果你这样做,你会得到同样的警告

float x = 3.0;

【讨论】:

  • 在我的编译器 (gcc) 上 float x = 3.0 不会产生错误
  • float x = 3.0; 中发生的转换称为转换cast 是导致转换的显式语法结构。 float x = (float) 3.0; 中有演员表,但float x = 3.0; 中没有演员表(有转换)。
  • 这不是错误,但某些编译器会警告您这是一个可能会丢失精度的隐式转换
  • @PascalCuoq 好吧,我想我用错了词。我听说过很多关于“隐式演员”这个词的事,但我猜它总是被不当使用
  • 不存在“隐式强制转换”之类的东西。顺便说一句,在这种情况下,如果 FLT_RADIX 是 2 或 10 的幂,那么这些文字会被准确地表示——几乎可以肯定是(事实上 FLT_RADIX != 2 很少见)。
【解决方案3】:

您示例中的浮点数组初始化在 C 中很好,就像它使用 double 常量一样。对于手头的值,如果它是一个用float 常量初始化的double 数组也可以,因为手头的值完全可以表示为float。即使在最后一种情况下,编译器也没有理由发出警告,尽管开发人员费心输入与最终类型不匹配的附加后缀会让人感到奇怪和困惑。

简而言之,作者只是很明确,在float 数组的情况下,这很好。

对于用十进制写的任意值,您不想用double 常量初始化float 变量,因为“double-rounding”(其中“double”一词表示两次,而不是指相同的类型姓名)。如果按如下方式初始化f1f2,它们最终会得到不同的值,而f1实际上是最接近预期值1.01161128282547:

  float f1 = 1.01161128282547f;
  float f2 = 1.01161128282547;

解释是f2的情况下,十进制值1.01161128282547is first rounded到最近的double,再到最近的float。这两个步骤比直接舍入到最接近的float 引入更多错误,f1 就是这样初始化的。

【讨论】:

    【解决方案4】:
    1. 一些术语吹毛求疵。

      • 这不是赋值,而是初始化
      • {}(例如1.0f)之间的不是文字,它们是常量。 C 只有 string literalscompound literals,仅此而已。在 C 中1.0 是一个浮点常量
    2. 在 C 语言中,右侧的类型(赋值或初始化)永远不会受到左侧类型的影响。右侧的值的类型始终是独立确定的。它仅由如何指定该值来确定。从不考虑左侧。然后,一旦知道右侧尺寸的类型,原始值将转换为左侧的类型。换言之,常量1.0 始终是double 类型的常量,即使您使用它来初始化float 对象也是如此。

    3. 使用double 常量(如1.0)来初始化float 类型的对象并没有错。正如我上面所说,double 值将转换为float 类型。但是,一些编译器会发出有关该转换的警告,通知用户可能的精度损失。特别是为了消除警告人们在初始化float 对象时会使用显式浮点后缀(例如1.0f)或显式转换为float(例如(float) 1.0)。这看起来不太好,因为它违反了 DRY(不要重复自己)规则,但在现实生活中,这种处理警告的方法经常遇到。

    【讨论】:

    • 初始化不只是一种赋值吗?我在我的问题中说分配是因为我认为它适用于所有分配,而不仅仅是初始化。
    • 感谢您指出 C 标准不使用单词 literal 表示浮点常量。
    猜你喜欢
    • 2020-07-27
    • 1970-01-01
    • 1970-01-01
    • 2012-07-11
    • 1970-01-01
    • 2013-09-08
    • 1970-01-01
    • 1970-01-01
    • 2013-09-19
    相关资源
    最近更新 更多