我在计算机系统课程中,并且一直在与Two's Complement一起挣扎.我想了解它,但我读过的所有内容并没有为我提供图片.我已经阅读了维基百科文章和其他各种文章,包括我的教科书.
因此,我想开始这个社区wiki帖子来定义Two's Complement是什么,如何使用它以及它如何在诸如强制转换(从有符号到无符号,反之亦然)等操作中影响数字,逐位操作和位移操作.
我所希望的是一个清晰简洁的定义,程序员很容易理解.
binary computer-science bit-manipulation twos-complement data-representation
我有一个简单的程序:
#include <stdio.h>
#define INT32_MIN (-0x80000000)
int main(void)
{
long long bal = 0;
if(bal < INT32_MIN )
{
printf("Failed!!!");
}
else
{
printf("Success!!!");
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
条件if(bal < INT32_MIN )总是如此.这怎么可能?
如果我将宏更改为:
#define INT32_MIN (-2147483648L)
Run Code Online (Sandbox Code Playgroud)
有谁可以指出这个问题?
对于学校项目,我要编写C函数printf.事情进展顺利,但有一个问题我找不到合适的答案,所以我在这里.
printf("PRINTF(d) \t: %d\n", -2147483648);
Run Code Online (Sandbox Code Playgroud)
告诉我(gcc -Werror -Wextra -Wall):
error: format specifies type 'int' but the argument has type 'long'
[-Werror,-Wformat]
printf("PRINTF(d) \t: %d\n", -2147483648);
~~ ^~~~~~~~~~~
%ld
Run Code Online (Sandbox Code Playgroud)
但是如果我使用int变量,一切都很顺利:
int i;
i = -2147483648;
printf("%d", i);
Run Code Online (Sandbox Code Playgroud)
为什么?
我理解了许多观点,他们非常有趣.无论如何,我猜printf是使用<stdarg.h>librairy,所以,va_arg(va_list ap, type)也应该返回正确的类型.对于%d和%i,显然返回的类型是int.它有什么改变吗?
我正在学习C++中的函数重载,并遇到了这个问题:
void display(int a)
{
cout << "int" << endl;
}
void display(unsigned a)
{
cout << "unsigned" << endl;
}
int main()
{
int i = -2147483648;
cout << i << endl; //will display -2147483648
display(-2147483648);
}
Run Code Online (Sandbox Code Playgroud)
根据我的理解,该int范围内给出的任何值(在我的情况下int是4个字节)都将调用,display(int)并且该范围之外的任何值都将是不明确的(因为编译器无法决定调用哪个函数).int除了最小值之外,它对整个值范围有效,即-2147483648编译因错误而失败
超载的召唤
display(long int)是模棱两可的
但是将相同的值与a int打印并给出值2147483648.我对这种行为感到困惑.
为什么只有在传递最负数时才会观察到这种行为?(该行为是相同的,如果一个short使用具有-32768-事实上,在负数和正数具有相同的二进制表示任何情况下)
使用的编译器:g ++(GCC)4.8.5
我对这个代码的MSVC和clang之间的行为差异感到困惑:
#include <iostream>
#include <cstdint>
int main() {
int64_t wat = -2147483648;
std::cout << "0x" << std::hex << wat << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Visual Studio 2010,2012和2013(在Windows 7上)全部显示:
0x80000000
Run Code Online (Sandbox Code Playgroud)
但是clang 503.0.40(在OSX上)显示:
0xffffffff80000000
Run Code Online (Sandbox Code Playgroud)
根据C++标准,正确的行为是什么?文字应该是零扩展还是符号扩展到64位?
我知道这int64_t wat = -2147483648LL;会在两个编译器上产生相同的结果,但我想知道没有文字后缀的正确行为.
我的片段:
auto i = -2147483648;
int j = 3;
std::swap(i, j); // Compile error about mismatched types here.
Run Code Online (Sandbox Code Playgroud)
编译器声明文字i是a long long.这是为什么?-2147483648适用于intMSVC x64.
我的编译器是MSVC,目标是64位.
该表达式可在标准中的§8.5.4/ 7中的示例中找到(N3797)
unsigned int ui1 = {-1}; // error: narrows
Run Code Online (Sandbox Code Playgroud)
鉴于§8.5.4/ 7及其第4个要点:
缩小转换是隐式转换:
- 从整数类型或未范围的枚举类型到不能表示原始类型的所有值的整数类型,除非源是一个常量表达式,其整数提升后的值将适合目标类型.
我想说这里没有缩小,因为-1是一个常量表达式,其积分提升后的值适合无符号整数.
另见关于积分促销的 §4.5/ 1 :
如果int可以表示源类型的所有值,则除了bool,char16_t,char32_t或wchar_t之外的整数类型的prvalue(其整数转换等级(4.13)小于int的等级)可以转换为int类型的prvalue ; 否则,源prvalue可以转换为unsigned int类型的prvalue.
从4.13开始,我们得到-1(一个int)的等级等于unsigned int的等级,因此它可以转换为unsigned int.
编辑
不幸的是,Jerry Coffin从这个帖子中删除了他的答案.我相信他是在正确的轨道上,如果我们接受这一事实,即在标准的这一变化之后,§8.5.4/ 7中第4个要点的当前读数是错误的.
数据类型int的最小值为-2,147,483,648.
所以,我输入了
int val = -2147483648;
Run Code Online (Sandbox Code Playgroud)
但是,它有一个错误:
unary minus operator applied to unsigned type.result still unsigned
Run Code Online (Sandbox Code Playgroud)
我该如何解决?
由于 C++20 二进制补码表示是标准允许的唯一表示,保证范围从 -2 N-1到 +2 N-1 -1。因此,对于 64 位有符号整数类型,范围从-9'223'372'036'854'775'808到9'223'372'036'854'775'807。但是,此代码不能在 Visual Studio 上编译(也不能在 gcc 上编译)
int main()
{
long long x{-9'223'372'036'854'775'808LL};
// error C4146: unary minus operator applied to unsigned type, result still unsigned
// error C2397: conversion from 'unsigned __int64' to '__int64' requires a narrowing conversion
}
Run Code Online (Sandbox Code Playgroud)
然而,如果我用long long x{-9'223'372'036'854'775'807LL - 1}compiles替换代码就好了并x保持正确的值。我没有得到什么?
#define INT_MAX 2147483647
#define INT_MIN (-INT_MAX-1)
Run Code Online (Sandbox Code Playgroud)
我在看csapp这本书的时候,发现作者提出了这个问题,但是我不明白为什么要这么定义。-2147483647和直接用定义有什么区别?