0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

计算机系统对数值类型的编码、运算、转换原理介绍1

jf_78858299 来源:元闰子的邀请 作者:元闰子 2023-05-09 16:30 次阅读

前言

在日常编程中, 数值类型numeric types )是我们打交道最多的类型,可能没有之一。除了最熟悉的 int,还有 longfloatdouble 等。正因太熟悉,我们往往不会深究它们的底层原理。因为平时的工作中,知道个大概,也够用了。

但,在某些业务场景下,比如金融业务,数值运算不准确会带来灾难性的后果。这时,你就必须清楚数值类型的二进制表示、截断、转型等原理,否则很难保证运算结果的正确性。

另外,数值类型也是一个容易被黑客攻击的点,考虑如下一段代码:

// C++
/* Declaration of library function memcpy */
void *memcpy(void *dest, void *src, size_t n);
/* Kernel memory region holding user-accessible data */
#define KSIZE 1024
char kbuf[KSIZE];
/* Copy at most maxlen bytes from kernel region to user buffer */
int copy_from_kernel(void *user_dest, int maxlen) {
    /* Byte count len is minimum of buffer size and maxlen */
    int len = KSIZE < maxlen ? KSIZE : maxlen;
    memcpy(user_dest, kbuf, len);
    return len;
}

如果你熟悉数值类型的原理,一定会敏锐察觉出第 10 行存在 intsize_t 的类型转换。在 64 位系统中,size_t 通常被定义为 unsigned long 类型,如果攻击者在调用 copy_from_kernel 时,特意传入一个负数的 maxlen,转型到 memcpy 中的 n 将会是一个很大的正数,从而导致了内存拷贝的越界!

数值类型是计算机编程的基础,用的很多,也很重要,理解它的底层原理,有助于写出正确的代码,避免一些意料之外的错误

每个计算机系统都有固定的 word size ,也即常说的 xx 位,它也是指针的大小,跟 虚拟内存 相关,比如一个 w 位系统上的应用程序,最多能够访问 byte 大小的虚拟内存。

最常用的是 32 位 和 64 位 系统,某些数值类型在它们之上会有些差异,比如 long 类型 在 32 位系统上是 32 bit 大小,在 64 位系统上是 64 bit 大小。 考虑如今 64 位系统逐渐成为主流,本文会以它作为基础,进行数值类型的介绍

整数

在计算机系统中,整数可以分成 无符号unsigned )整数 和 有符号signed )整数 两大类,这之下,按照类型表示的 bit 位大小,又可细分成 8 位的 char/byte/int8 、16 位的 short/innt16、32 位的 int/int32 和 64 位的 long/int64,它们的取值范围如下:

类型 最小值 最大值
[signed] char -128 127
unsigned char 0 255
short -32,768 32,767
unsigned short 0 65,535
int −2,147,483,648 2,147,483,647
unsigned int 0 4,294,967,295
long −9,223,372,036,854,775,808 9,223,372,036,854,775,807
unsigned long 0 18,446,744,073,709,551,615

死记这个表不容易,下面我们将试图从二进制编码层面去理解它。

二进制编码

整数在计算机系统上都是以二进制存储的,对于一个 w 位的整数 ,它的二进制表示写成这样:

其中, 取值 或 。

无符号编码(Unsigned Encodings)

在二进制表示的基础上,无符号编码 是这样:

比如,w = 4 场景下的一些例子:

图片

由上述可知, 无符号编码无法表示负数,因此只能表示无符号整数 。为了表示有符号整数,还要探寻另一种编码方式。

原码编码(True Form Encodings)

为了区分正数和负数,很容易想到使用一个 bit 位作为 符号位 , 表示正数, 表示负数。在无符号编码的基础上,使用最高位作为符号位,其他位含义不变,得出 原码编码 形式:

比如,w = 4 场景下的一些例子:

图片

虽然原码编码方式简单直观,但它还存在两个问题:

(1) 存在两种编码形式

原码编码方式下, 存在两种编码形式, 和 。同一个整数值,却有两种编码,这对计算机系统来说没什么意义,反而是一种浪费。

(2)带负数的加法运算不正确

原码编码方式下,两个正数的加法没问题,一旦带上负数,结果就出错了:

图片

所以,原码编码方式,注定不会被使用。

补码编码(Two's-complement Encodings)

于是,补码编码 被发明,它也是建立在无符号编码的基础上,仍然取最高位为符号位,编码方式是这样:

它与无符号编码的唯一区别是,最高位的取值从 变成了

比如,w = 4 场景下的一些例子:

图片

补码编码很巧妙地解决了原码编码的两个问题:

首先,0 在补码编码下只有一种编码形式, 。

此外,带负数的加法运算,也正确了。

图片

因为补码编码的简单和正确性,目前,几乎所有的计算机系统,都采用补码编码来表示有符号整数

位运算

位运算主要包含 取反异或移位 等几种,我们在业务开发时用得比较少,但如果你有阅读开源代码的习惯,就会经常发现它们的踪迹。如果碰巧对位运算不熟悉,那么阅读这些代码,就同读天书一般。

取反(~)、与(&)、或(|)、异或(^)的规则比较简单:

图片

移位运算,可以分成 左移右移 两种,其中,右移又可分为 逻辑右移算术右移

左移(<<)运算,是对二进制整数,向左移若干位,高位丢弃,低位补零 。也即,对 左移 位,得到 。

比如,对 int i = -1 左移 10 位,会得到 i = -1024 的结果:

// Java语言
public static void main(String[] args) {
    int i = -1;
    System.out.println("Before << , i's value is " + i);
    System.out.println("i's binary string is " + Integer.toBinaryString(i));
    i <<= 10;
    System.out.println("After << , i's value is " + i);
    System.out.println("i's binary string is " + Integer.toBinaryString(i));
}
// 输出结果:
Before << , i's value is -1
i's binary string is 11111111111111111111111111111111
After << , i's value is -1024
i's binary string is 11111111111111111111110000000000

图片

在 C/C++ 中,两种右移操作符都是 >>,对无符号整数用的是逻辑右移,对有符号整数用的是算术右移;在 Java 中,逻辑右移的操作符是 >>>,算术右移的操作符是 >>。为了方便区分,下文统一用 Java 的表示方法。

逻辑右移(>>>)运算,是对二进制整数,向右移若干位,高位补零,低位丢弃 。也即,对 逻辑左移 k 位,得到 。

比如,对 int i = -1 逻辑右移 10 位,会得到 i = 4194303 的结果:

// Java语言
public static void main(String[] args) {
    int i = -1;
    System.out.println("Before >>> , i's value is " + i);
    System.out.println("i's binary string is " + Integer.toBinaryString(i));
    i >>>= 10;
    System.out.println("After >>> , i's value is " + i);
    System.out.println("i's binary string is " + Integer.toBinaryString(i));
}
// 输出结果:
Before >>> , i's value is -1
i's binary string is 11111111111111111111111111111111
After >>> , i's value is 4194303
i's binary string is 1111111111111111111111

图片

算术右移(>>)运算,是对二进制整数,向右移若干位,高位补符号位,低位丢弃 。也即,对 逻辑左移 k 位,得到 。

比如,对 int i = -1 算术右移 10 位,仍会得到 i = -1 的结果:

// Java语言
public static void main(String[] args) {
    int i = -1;
    System.out.println("Before >> , i's value is " + i);
    System.out.println("i's binary string is " + Integer.toBinaryString(i));
    i >>= 10;
    System.out.println("After >> , i's value is " + i);
    System.out.println("i's binary string is " + Integer.toBinaryString(i));
}
// 输出结果:
Before >> , i's value is -1
i's binary string is 11111111111111111111111111111111
After >> , i's value is -1
i's binary string is 11111111111111111111111111111111

图片

目前为止,介绍移位运算的原理时,我们都默认 k < w,如果 k >= w 会怎样

比如, 左移 w 位,结果会是 吗:

// Java语言
public static void main(String[] args) {
    int i1 = -1;
    System.out.println("Before << 31, i1's value is " + i1);
    System.out.println("i1's binary string is " + Integer.toBinaryString(i1));
    i1 <<= 31;
    System.out.println("After << 31, i1's value is " + i1);
    System.out.println("i1's binary string is " + Integer.toBinaryString(i1));

    int i2 = -1;
    System.out.println("Before << 32, i2's value is " + i2);
    System.out.println("i2's binary string is " + Integer.toBinaryString(i2));
    i2 <<= 32;
    System.out.println("After << 32, i2's value is " + i2);
    System.out.println("i2's binary string is " + Integer.toBinaryString(i2));
}
// 输出结果:
Before << 31, i1's value is -1
i1's binary string is 11111111111111111111111111111111
After << 31, i1's value is -2147483648
i1's binary string is 10000000000000000000000000000000
Before << 32, i2's value is -1
i2's binary string is 11111111111111111111111111111111
After << 32, i2's value is -1
i2's binary string is 11111111111111111111111111111111

图片

上述例子中, w = 32,我们发现 k = 31 时,结果还符合预期;当 k = 32 时,结果不是 0,而是 -1,也即相当于 k = 0 时的结果。

原因是这样,w 位整数 x,当执行 x << k 时,实际执行的是 x << (k % w)。所以,当 i2 << 32 时,实际是 i2 << 32 % 32 = i2 << 0

右移操作也遵循同样的规则,也即 x >> k = x >> (k % w)x >>> k = x >>> (k % w)

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 二进制
    +关注

    关注

    2

    文章

    792

    浏览量

    41591
  • 计算机
    +关注

    关注

    19

    文章

    7404

    浏览量

    87688
  • 编程
    +关注

    关注

    88

    文章

    3586

    浏览量

    93576
  • 数值
    +关注

    关注

    0

    文章

    80

    浏览量

    14351
收藏 人收藏

    评论

    相关推荐

    计算机系统结构

    计算机系统结构
    发表于 05-09 19:03

    什么是计算机系统计算机硬件和计算机软件?

    第一章 计算机系统概论1. 什么是计算机系统计算机硬件和计算机软件?硬件和软件哪个更重要?解:P3计算
    发表于 07-22 09:06

    什么是计算机系统?硬件和软件哪个更重要?

    第一章计算机系统概论1 .什么是计算机系统计算机硬件和计算机软件?硬件和软件哪个更重要?解: P3计算
    发表于 07-26 07:18

    计算机系统中的软件系统

    专用计算机现代计算机运算速度最高可达每秒几万亿次几十亿次几亿次几万次计算机辅助制造是计算机应用领域之一其英文缩写是所谓的信息是指基本素材非数值
    发表于 09-13 07:22

    嵌入式计算机系统概述

    硬件子系统和软件子系统组成的,通过运行程序来协同工作计算机硬件:基本的计算机硬件系统运算器、控
    发表于 12-22 06:08

    简单介绍微型计算机的组成

    你了解自己的计算机?或者知道单片机的组成吗?这一小节主要简单介绍微型计算机的组成,以及微型计算机系统经常用到的概念,包括组成、工作过程、工作原理、
    发表于 01-10 07:11

    计算机系统概论

    1.1 计算机系统简介1.2 计算机的基本组成1.3 计算机硬件的主要技术指标1.4 本书结构
    发表于 04-11 09:31 0次下载

    微型计算机系统

             微型计算机系统与传统的计算机系统一样,也是由硬件系统和软件系统两大部分组成的。2.1
    发表于 03-03 08:31 0次下载

    什么是计算机系统的容错性

    什么是计算机系统的容错性             所谓容错是指在故障存在的情况下计算机系统不失效,仍然能够正常工作的特性
    发表于 01-08 13:49 1627次阅读

    深入理解计算机系统数值类型

    计算机系统中,整数可以分成 无符号(unsigned)整数 和 有符号(signed)整数 两大类,这之下,按照类型表示的 bit 位大小,又可细分成 8 位的 char/byte/int8
    的头像 发表于 08-19 15:17 1146次阅读

    计算机系统对数值类型编码运算转换原理介绍2

    。正因太熟悉,我们往往不会深究它们的底层原理。因为平时的工作中,知道个大概,也够用了。 但,在某些业务场景下,比如金融业务,数值运算不准确会带来灾难性的后果。这时,你就必须清楚数值类型
    的头像 发表于 05-09 16:31 877次阅读
    <b class='flag-5'>计算机系统</b><b class='flag-5'>对数值</b><b class='flag-5'>类型</b>的<b class='flag-5'>编码</b>、<b class='flag-5'>运算</b>、<b class='flag-5'>转换</b>原理<b class='flag-5'>介绍</b>2

    计算机系统对数值类型编码运算转换原理介绍3

    。正因太熟悉,我们往往不会深究它们的底层原理。因为平时的工作中,知道个大概,也够用了。 但,在某些业务场景下,比如金融业务,数值运算不准确会带来灾难性的后果。这时,你就必须清楚数值类型
    的头像 发表于 05-09 16:31 788次阅读
    <b class='flag-5'>计算机系统</b><b class='flag-5'>对数值</b><b class='flag-5'>类型</b>的<b class='flag-5'>编码</b>、<b class='flag-5'>运算</b>、<b class='flag-5'>转换</b>原理<b class='flag-5'>介绍</b>3

    计算机系统对数值类型编码运算转换原理介绍4

    。正因太熟悉,我们往往不会深究它们的底层原理。因为平时的工作中,知道个大概,也够用了。 但,在某些业务场景下,比如金融业务,数值运算不准确会带来灾难性的后果。这时,你就必须清楚数值类型
    的头像 发表于 05-09 16:31 479次阅读
    <b class='flag-5'>计算机系统</b><b class='flag-5'>对数值</b><b class='flag-5'>类型</b>的<b class='flag-5'>编码</b>、<b class='flag-5'>运算</b>、<b class='flag-5'>转换</b>原理<b class='flag-5'>介绍</b>4

    计算机系统中的关键组件有哪些

    计算机系统中,关键组件的协同工作构成了其强大的数据处理和运算能力。这些组件不仅决定了计算机的性能,还影响着用户的使用体验。以下是对计算机系统中关键组件的详细阐述,包括它们的定义、功能
    的头像 发表于 07-15 18:18 1259次阅读

    微处理器如何控制计算机系统

    微处理器,作为计算机系统的核心部件,承担着控制整个计算机系统运行的重要任务。它不仅是计算机运算中心,还是控制中心,负责执行程序指令、处理数据以及协调
    的头像 发表于 08-22 14:21 437次阅读