计算机基础之原码,反码,补码

自从从学校出来当了 Java 码农后,离这些经典又重要的计算机基础知识越来越远了。。。现在的情况是,连原码,反码,补码都傻傻分不清了!
最近在对 PHP 代码使用 Java 重构时,遇到了这么个问题:之前的 PHP 程序员在存储客户端 IPv4 地址时,是将获取到的点分十进制 IPv4 地址转换成了相应的整数存入数据库的,这么做的好处嘛就是节省空间,方便索引。“将点分十进制的 IPv4 地址转换成整数”,这在 PHP 中很简单,原生 API 支持,API 文档在此!但对于 Java 程序员来说就不是这么简单的了,因为 Java 并没有提供原生 API 的支持!
那么,现在只能考虑自己使用 Java 实现了:
首先,需要弄清楚 PHP 中 ip2long($ip) 的实现原理,经过参考某位仁兄的写的博客PHP: 详解ip2long和long2ip,基本上就了解其原理了。原理其实很简单:就是把点分十进制的 IPv4 地址字符串,按照其二进制形式放入一个四字节的整型变量中即可!上面那位仁兄博客里有用 C 语言实现了下 PHP 方法 ip2long(),直接使用逻辑左移即可,但输出的一正一负的结果却引起了我的兴趣(因为不理解嘛)。于是,我在例子上又加了一行:

#include <stdio.h>

int main(int argc, char** argv){
	unsigned int ip_long = (157 << 24) | (23 << 16) | (56 << 8) | 90;
	printf("%x\n", ip_long);
	printf("%u\n", ip_long);
	printf("%d\n", ip_long);

	return 0;
}

即输出整形变量 ip_long 的十六进制形式,运行结果如下:

9d17385a
2635544666
-1659422630

那么,该怎么解释着三行输出呢?此时便想起了原码,反码,补码的转换规则:

  1. 正数的原码,反码,补码都一样;
  2. 负数原码符号位不变,数值位按位取反的结果就是反码;
  3. 负数的反码加一便是其补码;
  4. 计算机中存储的数字的二进制形式是该数字的补码形式;

也就是说:数字 A 在计算机中存储的二进制形式是:数字 A 转换为二进制形式 B,此时 B 就是数字 A 的原码,再将 B 转换为反码 C,最后将反码 C 转换为补码 D,补码 D 就是数字 A 最终在计算机中的二进制形式。验证下这个说法,十六进制数 9d17385a 转换为二进制形式是:10011101000101110011100001011010

  1. 将变量 ip_long 解释为无符号整数,则 ip_long 即为正整数,则其原码,反码,补码都一样,所以推导出的 ip_long 的原码就是:10011101000101110011100001011010,转换为十进制即:2635544666,验证正确;
  2. 将变量 ip_long 解释为有符号整数,则从补码 10011101000101110011100001011010 的第一位即符号位为 1 可知 ip_long 是一个负数,再把补码 10011101000101110011100001011010 转换为其原码可得到原码:11100010111010001100011110100110,先不算符号位的 1,可得此数的绝对值的二进制是:1100010111010001100011110100110,转换为十进制是:1659422630,因为它是负数,所以最终结果是:-1659422630,验证正确;

参考

  1. PHP: 详解ip2long和long2ip
  2. Java ip2long equivalent
  3. 在线进制转换
posted @ 2017-06-15 19:29  clearbug  阅读(692)  评论(0编辑  收藏  举报