开始本章以前我先提个问题:“若是一个整数的绝对值等于它本身,那么这个数是几?”若是你回答是 0 和 全部正数,那么请你耐心读完这篇文章吧。 html
本章是我『代码之谜』系列的第二篇,前一篇『代码之谜 - 开篇/前言/序』简单介绍了计算机与数学的不一样。 java
数学中有许多复杂深入的矛盾,数学家的工做就是解释或者反驳这些矛盾, 例若有限与无限、连续与离散、存在与构造、逻辑与直观、具体与抽象、概念与计算等等。 linux
在本章中,咱们把目标缩小,主要讨论内容 编程
终于到主题了,也许你很想知道“负数的绝对值可能等于本身吗?”,也就是 “若是x等于-x,那么x有几个解?”按照我一向的做风,我是不会轻易告诉你答案的。 《编程珠玑》记载,做者告诉了他同事一个结果,而不是方法,最后追悔莫及。 因此,我在这里要告诉你方法,而不是告诉你答案。 编程语言
告诉你答案以前,首先得回答个问题:“整数(8bit)的表示范围是多少?”,(也许你已经把教科书的知识背下来了,是 -2^7 到 2^7-1,也就是 -128 到 +127,如今的计算机科学都快成为文科了^_^)。 翻译
若是你不知道也不要紧,至少你知道 8bit 能够表示的整数个数是 2^8 个,这个数等于多少无所谓,可是,它必定是个偶数(256)。 设计
那么这里就有一个颇有意思的问题了,0既不是正数也不是负数,把0去掉的话,整数的个数就是奇数了,整数还剩 255 个。 奇数个整数不可能平均分红两部分(正数和负数),要么负数多一个,要么正数多一个。事实就是,负数比正数多一个,最小的负数是 -128, 最大的整数是 127。 code
如今的问题是, -128 的绝对值是多少呢? -(-128)等于多少呢?是溢出呢,仍是等于它本身呢?也许计算机课本没有告诉你, 整数是不会出现溢出异常的,整数的溢出被认为是正常的舍弃(其实只要很合理)。整数只有被0除才会异常,而浮点数,即便被0除也不会抛出异常。 浮点数除0的操做将放在本系列浮点数篇讨论。 htm
绝对值等于本身的数有两个,0 和最小的负数。 对象
你可能要像香港电影里女主角那样歇斯底里的大喊“绝对值怎么多是负数呢? 不可能,我不信,我不信…”
忘掉你那可怜的数学知识吧,“发生这种事,你们都不想的。感情的事呢,是不能强求的。所谓吉人自有天相,作人呢,最要紧的就是开心…”跑题了,赶忙回来。
在经典数学(非皮亚诺算术系统,皮亚诺绝对是欧几里德的铁杆粉丝,要不怎么会有如此天才的构想,这个之后会给你们普及)中,绝对值定义为:“从原点到点A的距离,称为A的绝对值,它是一个非负数”。 既然讲到了距离,不妨剧透一下(本系列“逻辑篇”会涉及到),两个数的大小在数学中如何定义,“距离数轴原点的距离远近”,计算机中大小如何定义的呢?给你们留个做业吧(别告诉我是设计编程语言或者设计电脑的科学家规定的,计算机科学绝对不是文科)。
计算机中没有数轴,绝对值是如何定义的呢?看看java、C、Python的源码(感谢那些开源大牛),和我们学的小学数学同样。
abs(x) := (x >= 0) ? x : -x
翻译过来就是,x的绝对值定义为:正数和0的绝对值等于它本身,负数的绝对值等于-x。(这里使用的是-x,而没有用0-x,由于在浮点数中,这二者是有区别的。)
那么 -x 是如何计算的呢? 计算是数学概念,在计算机中,咱们应该说 -x是如何求值的呢?还得回到源码,我只看了linux中关于C的源码,若是你看过其它语言源码发现和我说的不一样,请联系我。 学过计算机原理的都知道,负数在计算机中以补码形式存储,计算补码的方式和取反操做相似。
符号位不变,其它位取反,最后加一。
好比 -5
原码: 1000,0101 其它位取反: 1111,1010 加一: 1111,1011
当咱们求它的绝对值时如何操做呢
补码: 1111,1011 这是-5在计算机中的表示 各位取反: 0000,0100 加一: 0000,0101 此时结果为+5
如今咱们回到最小的负数问题,最小的负数在计算机中表示为 1000,000,下面咱们对这个数操做
补码: 1000,0000 各位取反: 0111,1111 加一: 1000,0000
神奇吗,尼玛,竟然又回到本身了。