从 long double 到 unsigned long long 的转换在 MSVC C++ 中似乎已损坏。编译器

发布于 2025-01-11 19:43:44 字数 856 浏览 0 评论 0原文

考虑以下代码:

#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    long double test = 0xFFFFFFFFFFFFFFFF;
    cout << "1: " << test << endl;
    unsigned long long test2 = test;
    cout << "2: " << test2 << endl;
    cout << "3: " << (unsigned long long)test << endl;
    return 0;
}

使用 GCC g++ (7.5.0) 编译此代码并运行会按预期生成以下输出:

1: 1.84467e+19
2: 18446744073709551615
3: 18446744073709551615

但是,使用 Microsoft Visual C++ 编译器(16.8.31019.35,64 位和 32 位)编译此代码并运行产生以下输出:

1: 1.84467e+19
2: 9223372036854775808
3: 9223372036854775808

将值转换为 unsigned long long 时,MSVC 编译器不会给出大于(有符号)long 的最大值的值长

我做错了什么吗?

我是否遇到了我不知道的编译器限制?

有谁知道这个问题的可能解决方法?

Consider the following code:

#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    long double test = 0xFFFFFFFFFFFFFFFF;
    cout << "1: " << test << endl;
    unsigned long long test2 = test;
    cout << "2: " << test2 << endl;
    cout << "3: " << (unsigned long long)test << endl;
    return 0;
}

Compiling this code with GCC g++ (7.5.0) and running produces the following output as expected:

1: 1.84467e+19
2: 18446744073709551615
3: 18446744073709551615

However compiling this with the Microsoft Visual C++ compiler (16.8.31019.35, both 64-bit and 32-bit) and running produces the following output:

1: 1.84467e+19
2: 9223372036854775808
3: 9223372036854775808

When casting a value to an unsigned long long, the MSVC compiler won't give a value lager than the max of a (signed) long long.

Am I doing something wrong? 

Am I running into a compiler limitation that I do not know about?

Does anyone know of a possible workaround to this problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我恋#小黄人 2025-01-18 19:43:44

因为 MSVC long double 实际上只是一个 double(正如@drescherjm 在评论中指出的那样),它没有足够的精度来包含 0xFFFFFFFFFFFFFFFF 的精确值。当此值存储在 long double 中时,它会“四舍五入”为大于 0xFFFFFFFFFFFFFFFF 的值。当转换为 unsigned long long 时,这会导致未定义的行为。

Because a MSVC long double is really just a double (as pointed out by @drescherjm in the comments), it does not have enough precision to contain the exact value of 0xFFFFFFFFFFFFFFFF. When this value is stored in the long double it gets "rounded" to a value that is lager than 0xFFFFFFFFFFFFFFFF. This then causes undefined behaviour when converting to an unsigned long long.

梦明 2025-01-18 19:43:44

您看到未定义的行为,因为正如注释中所指出的,long double 与 MSVC 中的 double 以及 的“转换”值相同0xFFFFFFFFFFFFFFFF(或 ULLONG_MAX)实际上被“舍入”为一个稍大(但显着)的值,如以下代码所示:

int main(int argc, char* argv[])
{
    long double test = 0xFFFFFFFFFFFFFFFF;
    cout << 0xFFFFFFFFFFFFFFFFuLL << endl;
    cout << fixed << setprecision(16) << endl;
    cout << test << endl;
    return 0;
}

输出:

18446744073709551615
18446744073709551616.0000000000000000

因此,当将该浮点值转换回 unsigned long long 时,您将违反 此 Microsoft 文档

  • 对于转换为 unsigned longunsigned long long,转换为 out-of- 的结果范围值可以是除
    最高或最低可表示值。结果是否是
    哨兵或饱和值与否取决于编译器选项和
    目标架构。未来的编译器版本可能会返回饱和的
    或哨兵值。

通过切换到可在 Visual Studio 中使用的 clang-cl 编译器,可以进一步“验证”此 UB(因为需要一个更好的术语)。对于您的原始代码,这将为“2”和“3”输出行上的值提供0

假设 clang (LLVM) 编译器不受上述“微软规则”的约束,我们可以依靠 C++ 标准

7.10 浮点积分转换         [转换.fpint]

1    浮点类型的纯右值
可以转换为整数类型的纯右值。转换
截断;也就是说,小数部分被丢弃。行为是
如果截断值无法在中表示,则为未定义
目的地类型。

You are seeing undefined behaviour because, as pointed out in the comments, a long double is the same as a double in MSVC and the 'converted' value of your 0xFFFFFFFFFFFFFFFF (or ULLONG_MAX) actually gets 'rounded' to a slightly (but significantly) larger value, as can be seen in the following code:

int main(int argc, char* argv[])
{
    long double test = 0xFFFFFFFFFFFFFFFF;
    cout << 0xFFFFFFFFFFFFFFFFuLL << endl;
    cout << fixed << setprecision(16) << endl;
    cout << test << endl;
    return 0;
}

Output:

18446744073709551615
18446744073709551616.0000000000000000

Thus, when converting that floating-point value back to an unsigned long long, you are falling foul of the conversion rules specified in this Microsoft document:

  • For conversion to unsigned long or unsigned long long, the result of converting an out-of-range value may be some value other than the
    highest or lowest representable value. Whether the result is a
    sentinel or saturated value or not depends on the compiler options and
    target architecture. Future compiler releases may return a saturated
    or sentinel value instead.

This UB can be further 'verified' (for want of a better term) by switching to the clang-cl compiler that can be used from within Visual Studio. For your original code, this then gives 0 for the values on both the "2" and "3" output lines.

Assuming that the clang (LLVM) compiler is not bound by the aforementioned "Microsoft Rules," we can, instead, fall back on the C++ Standard:

7.10 Floating-integral conversions      [conv.fpint]

1     A prvalue of a floating-point type
can be converted to a prvalue of an integer type. The conversion
truncates; that is, the fractional part is discarded. The behavior is
undefined if the truncated value cannot be represented in the
destination type.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文