在 C 和 C++ 中使用浮点转换与浮点后缀之间有什么区别吗?

Is there any difference between using floating point casts vs floating point suffixes in C and C++?

这个有区别吗(使用floating point literal suffixes):

float MY_FLOAT = 3.14159265358979323846264338328f; // f suffix
double MY_DOUBLE = 3.14159265358979323846264338328; // no suffix 
long double MY_LONG_DOUBLE = 3.14159265358979323846264338328L; // L suffix

对比这个(使用浮点数转换):

float MY_FLOAT = (float)3.14159265358979323846264338328;
double MY_DOUBLE = (double)3.14159265358979323846264338328;
long double MY_LONG_DOUBLE = (long double)3.14159265358979323846264338328;

在 C 和 C++ 中?

注意:函数调用也是如此:

void my_func(long double value);

my_func(3.14159265358979323846264338328L);
// vs
my_func((long double)3.14159265358979323846264338328);
// etc.

相关:

  1. What's the C++ suffix for long double literals?
  2. https://en.cppreference.com/w/cpp/language/floating_literal

默认为double。假设 IEEE754 浮点数,doublefloat 的严格超集,因此您永远不会因为不指定 f 而失去精度。 编辑:仅当指定可由 float 表示的值时才适用。如果发生舍入,由于进行了两次舍入,这可能不是严格正确的,请参阅 Eric Postpischil 的 。因此,您还应该对浮点数使用 f 后缀。


这个例子也有问题:

long double MY_LONG_DOUBLE = (long double)3.14159265358979323846264338328;

这首先给出一个 double 常量,然后将其转换为 long double。但是因为你从 double 开始,你已经失去了永远不会回来的精度。因此,如果要在 long double 常量中使用全精度,则必须使用 L 后缀:

long double MY_LONG_DOUBLE = 3.14159265358979323846264338328L; // L suffix

使用后缀和强制转换是有区别的; 8388608.5000000009f(float) 8388608.5000000009 在常见的 C 实现中具有不同的值。此代码:

#include <stdio.h>

int main(void)
{
    float x =         8388608.5000000009f;
    float y = (float) 8388608.5000000009;
    printf("%.9g - %.9g = %.9g.\n", x, y, x-y);
}

打印“8388609 - 8388608 = 1”。在 Apple Clang 11.0 和其他使用正确舍入的实现中,IEEE-754 binary32 float 和 binary64 double。 (C 标准允许实现使用 IEEE-754 正确舍入以外的方法,因此其他 C 实现可能有不同的结果。)

原因是(float) 8388608.5000000009包含两个舍入操作。有了后缀,8388608.5000000009f直接转换为float,所以为了适应float必须丢弃的部分,.5000000009,直接检查看是否是否大于 0.5。是的,因此结果向上舍入为下一个可表示值 8388609。

没有后缀,8388608.5000000009先转换为double。当考虑必须丢弃的部分 .0000000009 时,发现它小于截断点低位的 ½。 (低位的值为.00000000186264514923095703125,一半是.000000000931322574615478515625。)所以结果向下舍入,我们有8388608.5作为double。当转换为 float 时,必须丢弃的部分是 .5,正好是可表示数字 8388608 和 8388609 之间的一半。打破平局的规则将其四舍五入为具有偶数位的值, 8388608.

(另一个例子是“7.038531e-26”;(float) 7.038531e-26不等于7.038531e-26f。这是唯一一个有效数字少于八位的数字,当float是binary32 和 double 是 binary64,当然“-7.038531e-26”除外。)

虽然在浮点常量中省略 f 不会降低精度,但这样做可能会出人意料。 考虑一下:

#include    <stdio.h>

#define DCN 0.1
#define FCN 0.1f
int main( void)
{
float   f = DCN;
    printf( "DCN\t%s\n", f > DCN ? "more" : "not-more");
float   g = FCN;
    printf( "FCN\t%s\n", g > FCN ? "more" : "not-more");
    return 0;
}

这(使用 gcc 9.1.1 编译)产生输出

DCN more
FCN not-more

解释是在 f > DCN 中,编译器将 DCN 设为双精度类型,因此将 f 提升为双精度,并且

(double)(float)0.1 > 0.1

就我个人而言,在(极少数)需要浮点常量的情况下,我总是使用 'f' 后缀。