浮動小数点型への型変換は精度が落ちる可能性がある 暗黙的な型変換 - 萌えJava超入門
第七章 基本データ型の型変換

浮動小数点型への型変換は
精度が落ちる可能性がある

浮動小数点型への型変換は、
int,long → float の変換と、long → double の変換で、
精度が落る場合があります。


目次

1.範囲の広さと表現できる精度

整数から浮動小数点型への変換は
誤差が出ることがあるんだ。
え?
広いデータ型に
型変換するのに?


扱える値の範囲がより広いデータ型への型変換は、
暗黙的(自動的)に行われます。
これは、値の損失が無いので、キャスト演算子を省略できるようになっているのです。

しかし、広いデータ型への型変換でも、僅かながら誤差が生じるケースがあります。

データ型の広さの順番
byte < short < int < long < float < double
左から右への型変換は暗黙的に行えます。

これは範囲の広さの順番ですが、
「範囲の広さ」と「表現できる精度」が逆転しているところがあります。
問題は浮動小数点型です。
↓の2つの表を見てみましょう。

プリミティブ型の範囲とサイズ
種類データ型サイズ
byte
範囲
整数型byte1
(8bit)
-128 ~ 127
整数型short2
(16bit)
-32768 ~ 32767
整数型int4
(32bit)
-2147483648
~ 2147483647
整数型long8
(64bit)
-9223372036854770000
~ 9223372036854775807
実数型float4
(32bit)
-3.4028235E+38
~ 3.4028235E+38
実数型double8
(64bit)
-1.79769313486231570E+308
~1.79769313486231570E+308
文字型char2
(16bit)
0~65535(UTF-16)
真偽型boolean-true 又は false


浮動小数点型のビットレイアウト
型名符号部指数部仮数部合計
float1bit 8bit23bit32bit
double1bit11bit52bit64bit

double の「範囲の広さ」は、
ダントツの -1.79769313486231570E+308~1.79769313486231570E+308 です。
しかし、整数の表現は、仮数部+1(正規化されたときに省かれた暗黙の1のビット)の53ビットに限られます。
広さは最強でも、精度では long に負けているのです

float の「範囲の広さ」は、
-3.4028235E+38 ~ 3.4028235E+38 です。
しかし、整数の表現は、仮数部+1の24ビットに限られます。
こちらも、精度では long と int に負けています


浮動小数点型のビットレイアウトは、こちらにも掲載しています
  Java超入門
    よく使いそうな資料一覧 浮動小数点型のビットレイアウト



2.int から double への変換は精度落ちしない

int は 4バイト32ビットの整数型です。
double は 8バイト64ビットの浮動小数点型です。
double は 64ビットの内、11ビットを指数部に充てているので、
double の整数の表現は、仮数部+1(正規化されたときに省かれた暗黙の1のビット)の53ビットに限られますが、
int の32ビットすべてを網羅できます。

オススメの intと doubleでは
暗黙的な型変換による誤差は
出ないぞ。
ぶっちゃけ
intと doubleを使えば
いいんじゃないっすか?
まあそうなんだけど、
続きも読んでよぅ。


3.int から float への変換による精度落ち

int は 4バイト32ビットの整数型です。
float は 4バイト32ビットの浮動小数点型です。
float は 32ビットの内、8ビットを指数部に充てているので、
float の整数の表現は、仮数部+1の24ビットに限られます。
int の32ビットの値をすべて表現することはできません。


floatの精度の境目を観察してみた。
ソースコードは気にしないで
結果だけ見てみよう。
//Sample09_62.java
class Sample09_62 {
    public static void main(String[] args){
        //Max 24bit 16777215
        int int_24 = 0b11111111_11111111_11111000;

        float f;
        for(int i=0; i<20; i++){
            f = int_24;
            System.out.println("int= " + int_24);
            System.out.println("float= " + f);
            System.out.println("-------------------");
            int_24++;
        }
    }
}

コマンドライン
 >javac Sample09_62.java
 >java Sample09_62
 int= 16777208
 float= 1.6777208E7
 -------------------
 int= 16777209
 float= 1.6777209E7
 -------------------
 int= 16777210
 float= 1.677721E7
 -------------------
 int= 16777211
 float= 1.6777211E7
 -------------------
 int= 16777212
 float= 1.6777212E7
 -------------------
 int= 16777213
 float= 1.6777213E7
 -------------------
 int= 16777214
 float= 1.6777214E7
 -------------------
 int= 16777215
 float= 1.6777215E7
-------------------       --ここから24ビットを超える--
 int= 16777216
 float= 1.6777216E7
 -------------------
 int= 16777217
 float= 1.6777216E7
 -------------------
 int= 16777218
 float= 1.6777218E7
 -------------------
 int= 16777219
 float= 1.677722E7
 -------------------
 int= 16777220
 float= 1.677722E7
 -------------------
 int= 16777221
 float= 1.677722E7
 -------------------
 int= 16777222
 float= 1.6777222E7
 -------------------
 int= 16777223
 float= 1.6777224E7
 -------------------
 int= 16777224
 float= 1.6777224E7
 -------------------
 int= 16777225
 float= 1.6777224E7
 -------------------
 int= 16777226
 float= 1.6777226E7
 -------------------
 int= 16777227
 float= 1.6777228E7
 -------------------
  

floatが表現しきれない値が飛び飛びになっています。

こんくらい
別に良くねっすか?


4.long から double への変換による精度落ち

long は 8バイト64ビットの整数型です。
double は 8バイト64ビットの浮動小数点型です。
double は 64ビットの内、11ビットを指数部に充てているので、
double の整数の表現は、仮数部+1の53ビットに限られます。
long の64バイトの値をすべて表現することはできません。


//Sample09_63.java
class Sample09_63 {
    public static void main(String[] args){
        //Max 53bit 9007199254740990L
        long long_53
          = 0b11111_11111111_11111111_11111111_11111111_11111111_11111000L;

        double dou;

        for(int i=0; i<20; i++){
            dou = long_53;
            System.out.println("long= " + long_53);
            System.out.println("double= " + dou);
            System.out.println("-------------------");
            long_53++;
        }
    }
}

コマンドライン
 >javac Sample09_63.java
 >java Sample09_63

 long= 9007199254740984
 double= 9.007199254740984E15
 -------------------
 long= 9007199254740985
 double= 9.007199254740985E15
 -------------------
 long= 9007199254740986
 double= 9.007199254740986E15
 -------------------
 long= 9007199254740987
 double= 9.007199254740987E15
 -------------------
 long= 9007199254740988
 double= 9.007199254740988E15
 -------------------
 long= 9007199254740989
 double= 9.007199254740989E15
 -------------------
 long= 9007199254740990
 double= 9.00719925474099E15
 -------------------       --ここから53ビットを超える--
 long= 9007199254740991
 double= 9.007199254740991E15
 -------------------
 long= 9007199254740992
 double= 9.007199254740992E15
 -------------------
 long= 9007199254740993
 double= 9.007199254740992E15
 -------------------
 long= 9007199254740994
 double= 9.007199254740994E15
 -------------------
 long= 9007199254740995
 double= 9.007199254740996E15
 -------------------
 long= 9007199254740996
 double= 9.007199254740996E15
 -------------------
 long= 9007199254740997
 double= 9.007199254740996E15
 -------------------
 long= 9007199254740998
 double= 9.007199254740998E15
 -------------------
 long= 9007199254740999
 double= 9.007199254741E15
 -------------------
 long= 9007199254741000
 double= 9.007199254741E15
 -------------------
 long= 9007199254741001
 double= 9.007199254741E15
 -------------------
 long= 9007199254741002
 double= 9.007199254741002E15
 -------------------
 long= 9007199254741003
 double= 9.007199254741004E15
 -------------------
  

doubleで表現しきれない値が飛び飛びになっています。

ふ~ん。
細かい話かもしれないが、
浮動小数点型への変換は
誤差が出るかもしれないと
覚えておいてくれ。


お疲れ様でした。




© 2019 awasekagami