在 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.
相关:
- What's the C++ suffix for long double literals?
- https://en.cppreference.com/w/cpp/language/floating_literal
默认为double
。假设 IEEE754 浮点数,double
是 float
的严格超集,因此您永远不会因为不指定 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' 后缀。
这个有区别吗(使用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.
相关:
- What's the C++ suffix for long double literals?
- https://en.cppreference.com/w/cpp/language/floating_literal
默认为double
。假设 IEEE754 浮点数,double
是 float
的严格超集,因此您永远不会因为不指定 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' 后缀。