'浮动a = 3.0;' 一个正确的声明?

TES*_*___ 86 c++ c++11

如果我有以下声明:

float a = 3.0 ;
Run Code Online (Sandbox Code Playgroud)

那是一个错误吗?我在一本书中读到了3.0一个double值,我必须将其指定为float a = 3.0f.是这样吗?

qua*_*dev 159

声明这不是错误float a = 3.0:如果这样做,编译器会将double literal 3.0转换为浮点数.


但是,您应该在特定方案中使用float literals表示法.

  1. 出于性能原因:

    具体来说,考虑:

    float foo(float x) { return x * 0.42; }
    
    Run Code Online (Sandbox Code Playgroud)

    在这里,编译器将为每个返回的值发出一个转换(您将在运行时支付).为了避免它,你应该声明:

    float foo(float x) { return x * 0.42f; } // OK, no conversion required
    
    Run Code Online (Sandbox Code Playgroud)
  2. 为了避免在比较结果时出现错误:

    例如,以下比较失败:

    float x = 4.2;
    if (x == 4.2)
       std::cout << "oops"; // Not executed!
    
    Run Code Online (Sandbox Code Playgroud)

    我们可以使用float文字表示法修复它:

    if (x == 4.2f)
       std::cout << "ok !"; // Executed!
    
    Run Code Online (Sandbox Code Playgroud)

    (注意:当然,这不是你应该如何比较浮点数或双数字的相等性)

  3. 要调用正确的重载函数(出于同样的原因):

    例:

    void foo(float f) { std::cout << "\nfloat"; }
    
    void foo(double d) { std::cout << "\ndouble"; }
    
    int main()
    {       
        foo(42.0);   // calls double overload
        foo(42.0f);  // calls float overload
        return 0;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 正如Cyber​​所指出的,在类型推导上下文中,有必要帮助编译器推导出float:

    在以下情况下auto:

    auto d = 3;      // int
    auto e = 3.0;    // double
    auto f = 3.0f;   // float
    
    Run Code Online (Sandbox Code Playgroud)

    同样,在模板类型扣除的情况下:

    void foo(float f) { std::cout << "\nfloat"; }
    
    void foo(double d) { std::cout << "\ndouble"; }
    
    template<typename T>
    void bar(T t)
    {
          foo(t);
    }
    
    int main()
    {   
        bar(42.0);   // Deduce double
        bar(42.0f);  // Deduce float
    
        return 0;
    }
    
    Run Code Online (Sandbox Code Playgroud)

现场演示

  • `float foo(float x){return x*42.0; ``可以编译成单精度乘法,并且是我上次尝试时由Clang编译的.但是`float foo(float x){return x*0.1; }`无法编译为单精度乘法.在此补丁之前可能有点过于乐观,但在补丁之后,只有当结果始终相同时,才应将conversion-double_precision_op-conversion组合到single_precision_op.http://article.gmane.org/gmane.comp.compilers.llvm.cvs/167800/match= (6认同)
  • 在第1点中,`42`是一个整数,它会自动提升为`float`(它将在编译时发生在任何体面的编译器中),因此没有性能损失.可能你的意思是'42.0`. (2认同)
  • @ChristianHackl将`4.2`转换为`4.2f`可能会产生设置`FE_INEXACT`标志的副作用,具体取决于编译器和系统,有些(实际上很少)程序确实关心哪些浮点运算是精确的,并且哪些不是,并测试该标志.这意味着简单明显的编译时转换会改变程序的行为. (2认同)

Cor*_*mer 22

编译器会将以下任何文字转换为浮点数,因为您将变量声明为浮点数.

float a = 3;     // converted to float
float b = 3.0;   // converted to float
float c = 3.0f;  // float
Run Code Online (Sandbox Code Playgroud)

如果您使用auto(或其他类型的扣除方法),例如:

auto d = 3;      // int
auto e = 3.0;    // double
auto f = 3.0f;   // float
Run Code Online (Sandbox Code Playgroud)

  • 使用模板时也会推断出类型,因此`auto`不是唯一的例子. (5认同)

Sha*_*our 14

没有后缀的浮点文字是double类型,这在草案C++标准部分2.14.4 浮动文字中有所介绍:

[...]浮点文字的类型是双精度,除非后缀明确指定.[...]

因此,它是分配错误3.0一个双重文字浮动

float a = 3.0
Run Code Online (Sandbox Code Playgroud)

不,它不会,它将被转换,这在4.8 浮点转换部分中有所介绍:

浮点类型的prvalue可以转换为另一个浮点类型的prvalue.如果源值可以在目标类型中准确表示,则转换的结果就是精确表示.如果源值在两个相邻目标值之间,则转换结果是这些值中任一个的实现定义选择.否则,行为未定义.

我们可以在GotW#67中阅读更多有关此含义的详细信息:双重或没有任何内容:

这意味着双常量可以隐式(即,静默)转换为浮点常量,即使这样做会失去精度(即数据).由于C兼容性和可用性原因,这被允许保留,但是当您进行浮点工作时,请记住这一点.

如果您尝试执行未定义行为的事情,质量编译器会发出警告,即将双倍数量放入小于最小值的浮点数,或者大于浮点数能够表示的最大值.一个非常好的编译器会提供一个可选的警告,如果你试图做一些可能被定义但可能丢失信息的东西,即将一个double数量放入一个浮点数,浮点数可以浮点数表示的最小值和最大值之间,但不能完全表示为浮点数.

因此,您应该注意的一般情况有一些警告.

从实际角度来看,在这种情况下,结果很可能是相同的,即使在技术上存在转换,我们可以通过在godbolt上尝试以下代码来看到这一点:

#include <iostream>

float func1()
{
  return 3.0; // a double literal
}


float func2()
{
  return 3.0f ; // a float literal
}

int main()
{  
  std::cout << func1() << ":" << func2() << std::endl ;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我们看到结果func1func2相同,使用clanggcc:

func1():
    movss   xmm0, DWORD PTR .LC0[rip]
    ret
func2():
    movss   xmm0, DWORD PTR .LC0[rip]
    ret
Run Code Online (Sandbox Code Playgroud)

正如Pascal在本评论中指出的那样,你不会总是指望这一点.使用0.10.1f分别导致生成的程序集不同,因为现在必须明确地进行转换.以下代码:

float func1(float x )
{
  return x*0.1; // a double literal
}

float func2(float x)
{
  return x*0.1f ; // a float literal
}
Run Code Online (Sandbox Code Playgroud)

导致以下程序集:

func1(float):  
    cvtss2sd    %xmm0, %xmm0    # x, D.31147    
    mulsd   .LC0(%rip), %xmm0   #, D.31147
    cvtsd2ss    %xmm0, %xmm0    # D.31147, D.31148
    ret
func2(float):
    mulss   .LC2(%rip), %xmm0   #, D.31155
    ret
Run Code Online (Sandbox Code Playgroud)

无论您是否可以确定转换是否会对性能产生影响,使用正确的类型更好地记录您的意图.例如,使用显式转换static_cast也有助于澄清转换意图与意外相反,这可能表示错误或潜在错误.

注意

作为supercat通过如指出,乘法0.10.1f是不等价的.我只是引用评论,因为它非常好,总结可能不会公正:

例如,如果f等于100000224(它可以精确地表示为浮点数),则将其乘以十分之一应该产生向下舍入到10000022的结果,但乘以0.1f将产生错误地向上舍入到10000023的结果如果意图除以10,则乘以双常数0.1可能比除以10f更快,并且比乘以0.1f更精确.

我原来的观点是在另一个问题中展示一个错误的例子,但这很好地说明了玩具例子中可能存在的微妙问题.