在C和C++中使用浮点转换与浮点后缀之间有什么区别吗?
这之间有区别吗(使用浮点文字后缀):
float MY_FLOAT = 3.14159265358979323846264338328f; // f suffix
double MY_DOUBLE = 3.14159265358979323846264338328; // no suffix
long double MY_LONG_DOUBLE = 3.14159265358979323846264338328L; // L suffix
vs 这个(使用浮点转换):
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.
有关的:
- 长双字面量的 C++ 后缀是什么?
- 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
- @GabrielStaples, That does complicate the compiler. Instead of having a long double literal just from lexing, it would now have to parse and special-case the cast, too. That's probably one fair reason why it doesn't work like that.
- Re "double is a strict superset of float, and thus you will never lose precision by not specifying f". That is not necessarily so because of potential *double-rounding* issues: A decimal floating-point literal first rounded to binary double precision with the result then rounded to binary single precision could produce a different result from same decimal floating-point literal with `f` suffix rounded to binary single precision right away.
- @GabrielStaples: double rounding can always cause a trouble. Consider 1.49. If you round it to the 0.1, then to integers, this happens: 1.49 -> 1.5 -> 2. But if you round it to an integer straight away, this happens: 1.49 -> 1. As you can see, the result is different. So this answer is not entirely correct as it ignores this fact. See Eric's answer for a more complete description. So the complete answer is this and Eric's answer combined.
回答
使用后缀和强制转换是有区别的;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 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”除外。)