なぜダブル歪ん?

序文

二重の歪みの例ではまず見て

public class DoubleTest {

    public static void main(String[] args) {
        for (double i = 0; i < 1; i = (i * 10 + 1) / 10) {
            for (double k = 0; k < i; k = (k * 10 + 1) / 10) {
                System.out.println(i + "-" + k + "=" + (i - k));
            }
        }
    }
}

出力:

0.1から0.0 = 0.1
0.2から0.0 = 0.2
0.2から0.1 = 0.1
0.3から0.0 = 0.3
0.3から0.1 = 0.19999999999999998
0.3から0.2 = 0.09999999999999998
0.4から0.0 = 0.4
0.4から0.1 = 0.30000000000000004
0.4から0.2 = 0.2
0.4から0.3 = 0.10000000000000003
0.5から0.0 = 0.5
0.5から0.1 = 0.4
0.5から0.2 = 0.3
0.5から0.3 = 0.2
0.5から0.4 = 0.09999999999999998
0.6から0.0 = 0.6
0.6から0.1 = 0.5
0.6から0.2 = 0.39999999999999997
0.6から0.3 = 0.3
0.6から0.4 = 0.19999999999999996
0.6から0.5 = 0.09999999999999998
0.7から0.0 = 0.7
0.7から0.1 = 0.6
0.7から0.2 = 0.49999999999999994
0.7から0.3 = 0.39999999999999997
0.7から0.4 = 0.29999999999999993
0.7から0.5 = 0.19999999999999996
0.7から0.6 = 0.09999999999999998
0.8から0.0 = 0.8
0.8から0.1 = 0.7000000000000001
0.8から0.2 = 0.6000000000000001
0.8から0.3 = 0.5
0.8から0.4 = 0.4
0.8から0.5 = 0.30000000000000004
0.8から0.6 = 0.20000000000000007
0.8から0.7 = 0.10000000000000009
0.9から0.0 = 0.9
0.9から0.1 = 0.8
0.9から0.2 = 0.7
0.9から0.3 = 0.6000000000000001
0.9から0.4 = 0.5
0.9から0.5 = 0.4
0.9から0.6 = 0.30000000000000004
0.9から0.7 = 0.20000000000000007
0.9から0.8 = 0.09999999999999998

まず、フロートは何ですか?

1、小数

小数点以下の組成:中国、小数は、3つの部分、すなわち、小数点整数+(セパレータ)+小数で表されます。
写真.PNG

2、それが呼び出された理由を浮動小数点小数

浮動小数点数の実数内の任意のコンピュータの近似表現に有理数表現の特定のサブセットに属しています。具体的には、この整数または固定小数点によって実数(すなわち、仮数)は、ベース(コンピュータで通常2)を乗じて得られた整数の乗、この表現は、ベース10に科学的表記法と同様です。

このような単純なフロートのために理解することができる。浮動小数点数は、任意の浮動小数点数です。

コンピュータ、のみバイナリの機械語では、機械語は、0と1を認識することができます。したがって、コンピュータの小数を保存する別の代替ストレージソリューションを必要としてすることは不可能です。:このプログラムは、インデックスプログラムで
写真.PNG
困難小数3.14として、上の写真を見て、見つけます。あなたは言葉(3.14E0)の指数形式を使用している場合は、その文言が変更されるので、それを書き、小数点が任意にフロートすることができます。

3、Javaの浮動小数点表現

フロートのために、4バイト、32ビットの仮数ビット0-22、23-30(8)は、指数を表し、31は、符号ビットを示しています。

ダブル8バイト、64ビットの仮数0から51、52-62(11)指数を表しているため、最上位63ビットは、符号ビットを表します。

第二には、メモリ内のフロートが保存する方法ですか?

私たちは、コンピュータメモリ内のデータが「0 \ 1」のストレージを使用している、フロートも真実であることを知っています。したがって、メモリ内の浮動小数点二進浮動小数点数を10進数に変換されなければなりません。

ストアへのバイナリ科学的表記のメモリ、注文コードこうして分割(すなわち、インデックス)及び塩基番号、正と負の点があるので、符号ビットがあるため。
例えばフロートでは、メモリ内のフロートが格納されている:
写真.PNG
フロート符号ビット(1ビット)、インデックス(8ビット)仮数部(23ビット)

二重符号ビット(1ビット)、インデックス(11ビット)仮数部(52ビット)

指数が実際にシフトインデックスに格納されているので、メモリ内の8ビットフロート、インデックスはEの真の値は、注文コードがEであり、E = E +(2 ^ N-1 -1)があることが想定されます。2 ^ N-1 -1所定のオフセットインデックスIEEE754規格であり、この式我々は2 ^ 8-1 = 127を得ることができます。したがって、-128 +127フロートのインデックス範囲は、二重指数関数1023へ-1024の範囲です。負の屈折率の絶対値が最小の非ゼロの浮動小数点数を表すことができる決定する工程;指数nは、浮動小数点で表現することができる最大数の絶対値を決定する、即ち、それは浮動小数点数の範囲を決定します。

フロート範囲から-2〜+2 ^ 128 ^ 127、即ち-3.40E + 38〜+ 3.40E + 38。

double的范围为-2^1024 ~ +2^1023,也即-1.79E+308 ~ +1.79E+308

这里使用移位存储,对于float来说,指数位加上127,double位加上1023(这里指的是存储,在比较的时候要分别减去127和1023)

移位存储本质上是为了保证+0和-0的一致性。

以float指数部分的这8位来分析,

那么这8位组成的新的字节,我们来用下面的一串数字表示:0000 0000

首先,我们假设不使用移位存储技术,而是单单看看这个 8位组成的新字节,到底能表示多少个数: 0000 0000 -1111 1111 即0-255,一共256个数。

但是我们知道这8位数既要表示正数也要表示负数。

所以将左边第一位拿出来表示正负的符号:

第一个区间:

0 000 0000 - 0 111 1111
即+0 到127

第二个区间:

1 000 0000 - 1 111 1111
即 -0到-127

这就是问题的所在:怎么会有两个0,一个正零,一个负零。

这时候使用移位存储:float使用127(0111 1111)

表示0:0+127=127 即 0000 0000 +0111 1111=0111 1111
表示1:1+127=128 即 0000 0001 +0111 1111=1000 0000
表示128:128+127=255 即 1000 0000+0111 1111=1111 1111

最大的正数,再大就要溢出了。

表示-1: -1+127=126=127-1 即 0111 1111-0000 0001=0111 1110
表示-1: -2+127=125=127-2 即 0111 1111-0000 0010=0111 1101
表示-127: -127+127=0 即0111 1111-0111 1111=0000 0000

最小的负数,在校就溢出了。

三、浮点数的进制转换

1、十进制转二进制

主要看看十进制转二进制,整数部分和小数部分分开处理

  • 整数部分:整数除以2,得到一个商和余数,得到的商继续除以2并得到一个商和一个余数,继续除以2操作直至商为0,上述操作得到一系列余数,从最后一个余数开始直至第一个余数,这一系列0\1即为转换后的二进制数。

  • 小数部分:乘以2,然后取出整数部分,将剩下的小数部分继续乘以2,然后再取整数部分,一直取到小数部分为零为止。如果永远不为零,则按要求保留足够位数的小数,最后一位做0舍1入。将取出的整数顺序排列。

从以上转换过程可以看出,并不是任何一个十进制小数都可以用二进制精确表示出来。一个在0到1之间的小数P可用如下形式表示:
写真.PNG
从这个式子中我们也可看出二进制表示出的小数是分段的,这也是为什么在Java中浮点数很多时候并不是十分精确的表示十进制小数的根本原因。

public static void main(String[] args) {
    float f1=20f;
    float f2=20.3f;
    float f3=20.5f;

    double d1=20;
    double d2=20.3;
    double d3=20.5;

    System.out.println(f1==d1);
    System.out.println(f2==d2);
    System.out.println(f3==d3);
}

true
false
true

以20.3举例:
20转换后变为 10100
0.3 要转换二进制,需要乘2, 乘完之后 取整数部分,然后用乘的结果减去整数部分, 然后 接着乘2, 直至最后没有小数或者小数出现循环, 即乘完.

0.3 * 2 = 0.6 (0)
0.6 * 2 = 1.2 (1)
0.2 * 2 = 0.4 (0)
0.4 * 2 = 0.8 (0)
0.8 * 2 = 1.6 (1)

计算到这里, 将再出现0.6,进入循环了,所以,结果
0.3 = 0.010011001…1001
所以20.3 = 10100.010011001…1001 (二进制).

2、二进制的科学记数法表示

20.3 = 10100.010011001…1001 (二进制)=1.01000100110011E10……(十进制科学计数)=1.01000100110011E100……(二进制科学计数)

这里使用移位存储,对于float来说,指数位加上127,double位加上1023(这里指的是存储,在比较的时候要分别减去127和1023)

同时要注意一点,以float为例,最高位表示的是整个数的符号位,指数位一共8位,最高位表示的是指数位的正负,因为有可能是E-100这样的情况,所以虽然有8位,最高位只是符号位,剩下7位才是表示真正的数值,这也是使用移位存储的原因。

对于一个数字,只要不超过和float的范围,同时小数部分不是无限小数,就可以和对应的double类型相等。

3、浮点数舍入规则

以52位尾数的双精度浮点数为例,舍入时需要重点参考第53位。

若第53位为1,而其后的位数都是0,此时就要使第52位为0;若第52位为0则不用再进行其他操作,若第52位为1,则第53位就要向52位进一位。

若第53位为1,但其后的位数不全为0,则第53为就要向第52位进一位。

若不是以上两种情况,也即53位为0,那么就直接舍弃不进位,称为下舍入。

浮点数舍入规则也就证明了为何在上文中提到的浮点数舍入中,相对舍入误差不能大于机器ε的一半。

对于java来说,一般float类型小数点后保留7位,而double类型小数点后保留15位。

这个原因也是因为尾数的数据宽度限制

对于float型来说,因为2^23 = 8388608

同时最左一位默认省略了,故实际能表示2^24 = 16777216个数,最多能表示8位,但绝对精确的只能表示7位。

而对于double型来说,2^52 = 4503599627370496,共16位。加上省略的一位,能表示2^53 = 9007199254740992。故double型最多能表示16位,而绝对精确的只能表示15位。

4、机器ε

机器ε表示1与大于1的最小浮点数之差。不同精度定义的机器ε不同。以双精度为例,

双精度表示1是

1.000…0000(52个0) × 2^0

而比1大的最小的双精度是(其实还能表示更小的范围,后文中会提到,但并不影响这里的机器ε)

1.000…0001 × 2^0

也即

2^-52 ≈ 2.220446049250313e-16。所以它就是双精度浮点数的机器ε。

在舍入中,相对舍入误差不能大于机器ε的一半。

对于双精度浮点数来说,这个值为0.00000005960464477539。

所以在Java中double类型中连续8个0.1相乘,就会出现表示不精确的情况。

个人博客
腾讯云社区
掘金
简书
公众号:wx.jpg

参考:
https://baijiahao.baidu.com/s?id=1618173300159774003&wfr=spider&for=pc
https://www.cnblogs.com/Vicebery/p/9997636.html
https://blog.csdn.net/Return_head/ Articleこの記事だった/詳細/ 88623060
https://blog.csdn.net/u011277123/article/details/95774544
https://blog.csdn.net/endlessseaofcrow/article/details/81269079

公開された19元の記事 ウォンの賞賛1 ビュー405

おすすめ

転載: blog.csdn.net/Devilli0310/article/details/103872860