浮点数的格式

Java 中基本类型中,表示小数的有 float 和 double。与他们相关的几个问题会经常出现,并且困扰着我们,例如:float 和 double 为什么叫做浮点数?他们的格式是什么样的?如何把一个十进制小数转化为二进制?为什么会出现 NaN 这中奇怪的东西?

在接下的的章节中,我们将逐一讨论这些问题。


1. 浮点数

准确的说,“浮点数”是一种表示数值的方法,类似的还有“定点数”。定点数是指数值中的小数点位置固定的数值表示方式。例如 3.141 和 2.500 是小数点在三位小数之前的定点数。而浮点数就是相对定点数来说,数值中的小数点位置不固定的数值表示方式。例如,2.500 就可以表示为 0.25* 10^1 或 25 * 10^-1。

可以看出,定点数在数值的表示上非常的“死板”,小数点的位置决定了小数部份的位数,并且在小数位数不足的情况下,还需要使用 0 来补全。浮点数则不存在这样的问题。

2. 十进制转二进制

十进制数转二进制通常是将整数部分和小数部份分开处理,先将整数部份转化为二进制,然后将小数部份转化为二进制,最后将整数部份和小数部份的结果和并起来。

对于整数部份来说,十进制转二进制通常使用除 2 取余数的方式来计算。如图 1 。

图 1

图 1 展示了十进制整数 26 转化为二进制整数 11010 的过程。十进制整数转二进制整数的基本过程是将十进制整数除以 2 ,记录余数后将商再除以 2 记录余数,如此反复,直至商小于 2 为止,将这个得到的最后的商也记录,最后把所有记录下的数翻转,即得到与十进制对应的二进制形式。图 1 中的具体过程如下:

1. 将 26 除以 2,商 13 余数为 0,记 0,累计为 0;

2. 将上一步的商做为被除数 13 除以 2,商 6 余数为 1,记 1,累计为 01;

3. 将上一步的商做为被除数 6 除以 2,商 3 余数为 0,记 0,累计为 010;

4. 将上一步的商做为被除数 3 除以 2,商 1 余数为 1,记 1,累计为 0101;

5. 上一步的商 1 ,小于 2 ,不再做除法并记录商,记 1,累计为 01011;

6. 将所有记录的数字翻转,得 11010,即为 26 的二进制形式。

对于小数部份来说,十进制转二进制通常采用乘 2 取整数的方式来计算。如图 2 。

图 2

图 2 展示了十进制小数 0.8125 转化为二进制小数 0.1101 的过程。十进制小数转二进制小数的基本过程是将十进制小数乘以 2,记录整数部分后,将积减去其整数部分后再乘以 2 记录整数部分,如此反复,直至积等于 0 或到达指定精度 [1]。图 2 中具体过程如下:

1. 将 0.8125 乘以 2,积为 1.625,取整数部分记 1,累计 1;

2. 将上一步的积减去整数部分后得 0.625 ,乘以 2 ,积为 1.25,取整数部分记 1,累计 11;

3. 将上一步的积减去整数部分后得 0.25 ,乘以 2 ,积为 0.5,取整数部分记 0,累计 110;

4. 将上一步的积减去整数部分后得 0.5 ,乘以 2 ,积为 1,取整数部分记 1,累计 1101;

5. 积为 1 不再做乘法,记录加小数点为 0.1101,即为 0.8125 的二进制形式。

经过以上计算,分别计算出了 26 和 0.8125 的二进制形式,如果我们将其合并起来,就得到了 26.8125 的二进制形式 11010.1101。

3. 浮点数的格式

明白如何从数的十进制形式得到的二进制形式后,需要理解在 Java 中是如何存储浮点数的。Java 遵循 IEEE 754 标准,它将一个浮点数分为“符号”、“阶码”和“尾数”3 个部分,并且定义了浮点数的表达方式,如图 3。

图 3

图 3 中 d.dd...d 即尾数,β 为基数,e 为指数。二进制中的 β 必然为 2,因此 d 的值必然只能为 0 或 1,并且定义了小数点前的第一个 d 必须是 1 。接下来图 4 展示了双精度浮点数 [2] 的格式。

图 4

先说明图 3 与图 4 间的关联。根据浮点数的定义,图 4 中的“符号”与图 3 中的 ± 号相对应,用于表示浮点数是正数还是负数,正数为 0 负数为 1;图 4 中的“尾数”与图 3 中 d.dd...d 相对应,用于表示浮点数中的有效数值,根据定义,图 4 中的尾数将忽略图 3 中的小数点以及小数点前的一个 d,默认其总是为 1;图 4 中的“阶码”与图 3 中的 e 对应,根据定义,双精度浮点数的阶码 = e + 1023;最后图 4 中没有任何与图 3 中的 β 所对应的部分,而是在二进制中总是默认为 2 。

大致了解了浮点数的结构,接下来通过实践来验证一下。

在前面的章节中,有得出十进制的 26.8125 的二进制形式为 11010.1101。根据图 3 中给出的定义,必须满足小数点前有一位的形式,因此调整 11010.1101 ,使其变为 1.10101101 * 2^4 。至此,推导出“符号”、“阶码”和“尾数”3 个部分:

符号为 0。因为根据定义,正数为 0,负数为 1。

阶码为 10000000011。因为 e = 4,根据定义,阶码 = e + 1023,阶码为 1027 的二进制形式。

尾数为 10101101。因为 1.10101101 中需要忽略小数点以及前一个 d。

接下来,将计算所得的几个数值根据双精度浮点数的格式进行处理:阶码已经有 11 位,不需要补全;尾数只有 8 位,而图 4 中标明了位数需要 52 位,使用 44 个 0 补全。然后,编写一段代码,验证一下手动计算得到的结果是否可以还原为原始的 26.8125。[3]

图 5

4. 特殊值

浮点数定义中指定了几个特殊值用于表示 0、 NaN 和 Infinity :

0 为阶码全为 0 且位数全为 0;

NaN 为阶码全为 1 且尾数不全为 0;

Infinity 为阶码全为 1 且尾数全为 0。

根据定义,浮点数应该存在 +Infinity、-Infinity、+NaN、-NaN、 +0、-0、等几种形式。可以使用如图 5 的方式一一验证所有的正特殊值。但由于图 5 中的代码在处理的中间使用了 long 作为过渡,因此 long 值如果为负数时,将会被表示为补码。根据源码补码转源码的定义(源码 = 补码 - 1 后除符号位取反),以 -Infinity 为例,则 -Infinity 符号为 1,阶码全为 1,尾数全为 0。如果使用原码表示,则为:

1111111111110000000000000000000000000000000000000000000000000000

如果此值为补码,转为源码后的表示则为:

1000000000010000000000000000000000000000000000000000000000000000

下面同样使用如图 5 的方法进行验证。

图 6


图 7

图 6 和图 7 分别验证了 +Infinity 和 -Infinity。由于 long 的补码原因,这个方式无法验证 -NaN 和 -0。[4]


[1] 例如双精度浮点数只有 52 位尾数用于表示这个运算的结果,因此这个运算是不可能无限继续下去。

[2] Java 中的双精度浮点数为 double。与双精度浮点数类似的,还有单精度浮点数、扩展精度浮点数等等。

[3] 此方法首先将指定的、表示二进制数值的字符串转化为一个 long 值,然后调用方法 Double.longBitsToDouble(long) 将这个 long 值转化为一个拥有相同二进制表示的 double 值。

[4] 这并不在这篇文章的讨论范围内,你可以运行代码 System.out.println(-Double.NaN== Double.NaN);System.out.println(Double.compare(-Double.NaN,  Double.NaN));System.out.println(-0.0d== 0.0d);System.out.println(Double.compare(-0.0d,  0.0d)); 比较并思考运行结果,或查阅其他资料。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容