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

Gab*_*les 6 c c++ floating-point casting

这之间有区别吗(使用浮点文字后缀):

float MY_FLOAT = 3.14159265358979323846264338328f; // f suffix
double MY_DOUBLE = 3.14159265358979323846264338328; // no suffix 
long double MY_LONG_DOUBLE = 3.14159265358979323846264338328L; // L suffix
Run Code Online (Sandbox Code Playgroud)

vs 这个(使用浮点转换):

float MY_FLOAT = (float)3.14159265358979323846264338328;
double MY_DOUBLE = (double)3.14159265358979323846264338328;
long double MY_LONG_DOUBLE = (long double)3.14159265358979323846264338328;
Run Code Online (Sandbox Code Playgroud)

在 C 和 C++ 中?

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

void my_func(long double value);

my_func(3.14159265358979323846264338328L);
// vs
my_func((long double)3.14159265358979323846264338328);
// etc.
Run Code Online (Sandbox Code Playgroud)

有关的:

  1. 长双字面量的 C++ 后缀是什么?
  2. https://en.cppreference.com/w/cpp/language/floating_literal

orl*_*rlp 5

默认值为double. 假设 IEEE754 浮点数double是 的严格超集float,因此不指定f. 编辑:这仅在指定可以由float. 如果发生舍入,由于舍入两次,这可能不是严格正确的,请参阅 Eric Postpischil 的回答。所以你也应该使用f浮点数的后缀。


这个例子也有问题:

long double MY_LONG_DOUBLE = (long double)3.14159265358979323846264338328;
Run Code Online (Sandbox Code Playgroud)

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

long double MY_LONG_DOUBLE = 3.14159265358979323846264338328L; // L suffix
Run Code Online (Sandbox Code Playgroud)

  • @GabrielStaples,这确实使编译器变得复杂。现在,它不再需要仅从词法分析中获得长双精度文字,而是还必须对强制转换进行解析和特殊情况处理。这可能是它不能那样工作的一个合理原因。 (2认同)
  • 关于“double 是 float 的严格超集,因此您永远不会因为不指定 f 而失去精度”。这不一定是这样,因为潜在的*双舍入*问题:十进制浮点文字首先舍入为二进制双精度,然后舍入为二进制单精度可能会产生与相同的十进制浮点文字不同的结果` f` 后缀立即四舍五入为二进制单精度。 (2认同)
  • @GabrielStaples:双重舍入总是会引起麻烦。考虑 1.49。如果将其四舍五入为 0.1,然后四舍五入为整数,则会发生这种情况:1.49 -> 1.5 -> 2。但是,如果立即将其四舍五入为整数,则会发生这种情况:1.49 -> 1。如您所见,结果是不同的。所以这个答案并不完全正确,因为它忽略了这个事实。有关更完整的描述,请参阅埃里克的回答。所以完整的答案是这个和埃里克的答案的结合。 (2认同)

Eri*_*hil 5

使用后缀和强制转换是有区别的;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);
}
Run Code Online (Sandbox Code Playgroud)

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

原因是它(float) 8388608.5000000009包含两个舍入操作。后缀,8388608.5000000009f直接转换为float,因此必须丢弃才能放入float0.5000000009 的部分,直接检查它是否大于 .5。是的,所以结果被四舍五入到下一个可表示的值,8388609。

没有后缀,8388608.5000000009首先转换为double. 当考虑必须丢弃的部分 .0000000009 时,发现它小于截断点低位的 1/2。(那里的低位值是.00000000186264514923095703125,一半是.000000000931322574615478515625。)所以结果四舍五入,我们有835886 double。当float强制转换为 时,必须丢弃的部分是 0.5,正好是可表示数字 8388608 和 8388609 的中间值。打破平局的规则将它舍入到具有偶数低位的值 8388608。

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