ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

库函数 | 大事件!std::abs() 失效

2021-10-05 13:34:40  阅读:144  来源: 互联网

标签:std 00 val int 取反 1111 abs 库函数


题记:10/4/2021 刷 LeetCode 时所遇,被一个奇怪的 case 阻挠,无法 AC

涉及:【原码/补码】【C 库函数 abs】

以下代码执行后结果为?

#include <cstdlib>
#include <iostream>
using namespace std;

int main()
{
    int val = (1 << (sizeof(int)*8 - 1) );	// -2147483648 = -2^31
    val = abs(val);
    cout << val << endl;
}

正确答案:-2147483648 ( abs() 处理后,val 没有发生任何变化)

(⊙ˍ⊙)?WHY?WHY?

迷雾追踪

库函数 std::abs()是绝对值函数,返回传入参数的绝对值!

但很显然,在上述代码中,std::abs()的取绝对值的能力失效了,所以当务之急是找出 std::abs()的实现。

(因为 std::abs()是库函数,而库函数是有 C 标准协会制定函数实现的标准,具体的实现内容,各个平台实现的可能不一致,这里我追寻的是 Linux 平台下的实现版本)

#include <stdlib.h>

#undef	abs

/* Return the absolute value of I.  */
int
abs (int i)
{
  return i < 0 ? -i : i;
}

return i < 0 ? -i : i

??? (⊙x⊙;) ???
-(-2147483648) 不等于 2147483648 吗?难道说,C 语言中的对数取负值的操作并没有我想的那样?

先把自己知道的罗列下:

  1. int 类型的变量大小 4 byte
  2. int 这类的整形变量,采用 “补码” 表示负数
  3. int 能够表示的数据范围:[ -2^31, -1 ] & [ 0, 2^31-1 ]
int val = 1;
val = -val;		// 这个过程是什么样的呢?

在计算机组成的相关书籍中有讲过 “负数用补码表示,补码 = 原码取反 + 1”。

这里我们把上述的理论直接带入看看会得到什么样的结果。

int val =  1;	0000:0001
    val = -val;	1111:1111 [正确结果]
/*
1. 取反 0000:0001 -> 1111:1110
2. 加一 1111:1110 -> 1111:1111 (吻合)
*/

这里我们仅测试了 “正数 -> 负数”,下面我们再测试下 “负数 -> 正数”

int val = -1;	1111:1111
    val = -val; 0000:0001 [正确结果]
/*
1. 减一 1111:1111 -> 1111:1110
2. 取反 1111:1110 -> 0000:0001 (吻合)
*/

现在把 2147483648带入我们刚掌握的计算过程中:

-2147483648 == 10-00-00-00
1. 减一 10-00-00-00 -> 7f-ff-ff-ff
2. 取反 7f-ff-ff-ff -> 10-00-00-00

_(:з)∠)_ AWSL。。。

按照 C 中取负的处理方法,-2147483648 确实会等于自身。

Emmmm,但是为什么会在出现这样的情况呢?貌似这个问题又回到计算机组成原理了。
当时学编码这块时,就学的不是很通透,看来这回要补课啦 >︿<

(以下为分析过程)

int type size = 4 byte = 4 * 8 = 32 bit = 2^32 种可表达数据
数据范围稍微有亿点大,我缩小点:
int type size = 0.5 byte = 4 bit = 16 种可表达数据

1111 [-1]----
1110 [-2]    |
1101 [-3]    |
1100 [-4]    |
1011 [-5]    |
1010 [-6]    |
1001 [-7]    |
1000 [-8]    |
=========    |
0111 [7]     |
0110 [6]     |
0101 [5]     |
0100 [4]     |
0011 [3]     |
0010 [2]     |
0001 [1]     |
0000 [0]-----

负数 -> 正数:-1、取反

正数 -> 负数:取反、+1

其实,这里最应该疑惑的是:

  1. 为什么我要 “取反”
  2. 为什么我要 ”+1“

现在请带着这些疑问看向上面的代码框,仔细分析下里面的规律。

不难发现:“补码表示的负数,跟正数,刚好差一格,就可以轴对称”!!!
这个时候再回过来看 C 语言提供的正负转换机制,可以发现 “+1/-1” 刚好就是为了让正负补齐差距,来达成轴对称的目标。

eg:

1 to -1
1>  1 取反,得 -2 【0001 -> 1110】
2> -2 加一,的 -1 【1110 -> 1111】
Snipaste_2021-10-05_13-12-53

总结:

迷雾追踪总共提炼了两个问题作为道标:

  1. 为什么 val = -2147483648 时,std::abs() 库函数会失效
  2. 为什么 C 语言提供的 “取负” 操作,会让 -2147483648 等于 ta self

问题 ① 解题后的报酬:

  1. “获得了 Linux 提供的 std::abs() 的具体实现”
  2. “C 的取负值操作是如何执行的”

问题 ② 解题后的报酬:

  1. “彻底明白了计算机组成中 补码 & 原码 换算公式是如何推导出来的”

标签:std,00,val,int,取反,1111,abs,库函数
来源: https://blog.csdn.net/qq_43483263/article/details/120613113

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有