【问题标题】:Why is C++ formatting of long variable treating it like int?为什么长变量的 C++ 格式将其视为 int?
【发布时间】:2020-04-21 14:13:20
【问题描述】:

在使用更大的程序时,我发现了一个行为异常的片段:

long L = 2500000000L;
fprintf( stderr, "L is %ld\n", L );
fflush( stderr );

由于 L 的声明是 long 并且格式是 %ld,我希望看到这个打印像“2500000000”,但它实际上打印了

L is -1794967296

这更符合我对%d 格式的期望。

但是,如果我将格式更改为 %uld,它会正确打印 2500000000,尽管编译器会发出警告说 L 应声明为 unsigned

这是 C++ 格式的错误吗?我在 Windows 上使用 Eclipse + mingw,将方言指定为 c++11。

【问题讨论】:

  • 也许你应该添加一个断言 long 的大小足以容纳该数字并且它不仅仅是 32 位整数(在这种情况下它将超出正数 long 的范围数字)(长为 32 位是 Windows 上的常见情况)。 static_assert(8 == sizeof(L), "too small");
  • 带符号的 long 不能保存该值,因此您会看到未定义的行为在第一种情况下表现为环绕,在第二种情况下表现为您的预期值。
  • en.cppreference.com/w/cpp/language/types,long在windows上通常是32位,在64位macos和Linux上只有64位
  • 使用直接列表初始化来防止潜在的缩小可能也是一个好主意long L{2500000000L};
  • 我相信 2,500,000,000(请检查我的 0 计数)略大于 2**31-1,最大的 32 位有符号整数,但应该很好地适合有符号 64 的范围位整数。 user7860670 建议的断言没有失败。

标签: c++ formatting


【解决方案1】:

2'500'000'000 需要 32 位来表示。在某些机器上(尤其是在 Windows 机器上),long 是 32 位宽,并且为符号位预留了 1 位,因此您只剩下 31 位,这还不够。要解决此问题,您可以根据需要使用unsigned longlong longstd::int64_t

【讨论】:

  • 符号位部分不正确。整数通常以二进制补码形式表示,而不是符号大小。
  • @sparik 二进制补码也需要 1 个符号位。
  • 对不起,我不小心编辑了你的答案,而不是我的 :)
  • @eerorika 拥有强大(编辑)能力,责任重大:)
【解决方案2】:

long 数据类型需要能够在 C++ 中表示的最大数字是 2'147'483'647。换句话说,它必须至少为 32 位宽。 2'500'000'000 超出了该范围。

窗户

特别是在 windows 中,long 是一个 32 位的数字,将无法表示 2'500'000'000。

这是 C++ 格式的错误吗?

看起来不像。

但是,如果我将格式更改为 %uld,它会打印出来

如果你这样做,那么程序的行为将是未定义的,因为格式说明符与参数的类型不匹配。您还必须更改整数的类型。

unsigned long 保证能够在所有系统上表示 2'500'000'000。


如果您希望能够表示 64 位数字,那么最便携的选择是 long longstd::int_least64_tstd::int_fast64_t,所有这些都保证至少为 64 位。

【讨论】:

  • 我会转发一些我的咖啡,如果我能再发表评论的话。
  • 在哪些方面int_least64_tint_fast64_t 更可取?两者似乎都至少 64 位宽,而后者理论上可能更快。
  • int_fast64_tint_least64_tprintf 中都缺少长度说明符。 intmax_t 有一个。
  • @TedLyngmo PRIdFAST64PRIdLEAST64
  • @eerorika Ahhh...我忘了那些:)
【解决方案3】:

没有错误。这仅意味着系统中的带符号类型 long int 与 int 类型具有相同的宽度。因此,long int 类型的对象无法表示这样的正数。

因此,您可以使用 long long int 类型来代替 long int 类型

long long L = 2500000000;
fprintf( stderr, "L is %lld\n", L );

请注意,最好从整数文字中排除后缀“L”。编译器自己会决定常量的类型。

如果整数文字没有后缀,则其类型由 这些类型相对于哪个类型可以表示值

int int
long int
long long int

要检查哪种有符号整数类型可以容纳这样的常量,请运行以下演示程序

#include <iostream>
#include <limits>

int main() 
{
    std::cout << 2500000000 << '\n';
    std::cout << std::numeric_limits<int>::max() << '\n';
    std::cout << std::numeric_limits<long>::max() << '\n';
    std::cout << std::numeric_limits<long long>::max() << '\n';

    return 0;
}

确定常量类型的另一种方法是使用在标头&lt;type_trais&gt; 中声明的标准类std::is_same

这是一个演示程序

#include <iostream>
#include <iomanip>
#include <type_traits>

int main() 
{
    std::cout << 2500000000 << '\n';

    std::cout << std::boolalpha << std::is_same<int, decltype( 2500000000 )>::value << '\n';
    std::cout << std::boolalpha << std::is_same<long, decltype( 2500000000 )>::value << '\n';
    std::cout << std::boolalpha << std::is_same<long long, decltype( 2500000000 )>::value << '\n';

    return 0;
}

因此,如果使用 printf 调用需要为输出对象指定转换说明符,您可以编写

auto L = 2500000000;

if ( std::is_same<long, decltype( L )>::value )
{
    fprintf( stderr, "L is %ld\n", L );
}
else if ( std::is_same<long long, decltype( L )>::value )
{
    fprintf( stderr, "L is %lld\n", L );
}

【讨论】:

  • “这只意味着你系统中的 long int 类型与 int 类型具有相同的宽度。” OP 没有说什么。
  • @Ayxan 不,他没有这么说。他说如果使用说明符 %d 输出是一样的。
  • OP 哪里说的?我只看到一个部分声称如果他们使用%d,他们会期望这样的输出
  • @Ayxan 是的。但他支持 long 类型比 int 类型更宽。 .
猜你喜欢
  • 2023-04-08
  • 2021-05-04
  • 2017-03-01
  • 1970-01-01
  • 2021-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多