2012年4月17日火曜日

NUM13-J. プリミティブ整数を浮動小数点数に変換する際、精度を低下させない


NUM13-J. プリミティブ整数を浮動小数点数に変換する際、精度を低下させない

以下に挙げる19の型変換は、拡張プリミティブ型変換(widening primitive conversion)と呼ばれる。

  • byte から short, int, long, float, double のいずれかへの変換
  • short から int, long, float, double のいずれかへの変換
  • char から int, long, float, double のいずれかへの変換
  • int から long, float, double のいずれかへの変換
  • long から float,double のいずれかへの変換
  • float から double への変換

intあるいはlongからfloatへ、もしくはlongからdoubleへの型変換は、精度の低下(最下位ビットが失われる)を伴う。これらの変換では、変換後の浮動小数点数は、IEEE 754による最近接丸め(round-to-nearest)モードに基づき、整数を丸めた値となる。精度の低下が発生するにも関わらず、Java言語仕様では、実行時例外をスローすることなく変換や丸めこみを行うことを要求している。詳しくはJava言語仕様§5.1.

javaの文字列から16進数の値を確認する方法
2「拡張プリミティブ変換」を参照。intより小さな整数型から浮動小数点型への変換や、intからdoubleへの変換では精度の低下は発生しない。したがって、intもしくはlongから浮動小数点数型への変換あるいは、longからdoubleへの変換において、必要とされる精度が低下しないように注意しなくてはならない。

floatからdoubleへの変換においても絶対値を表すビットの一部が失われる可能性があることに注意。詳細は「NUM06-J. どのプラットフォームでも一貫した浮動小数点数演算を行うために strictfp 修飾子を使う」を参照。

違反コード

以下の違反コードでは、2つの大きな、同じ値の整数リテラルをsubFloatFromInt()メソッドの引数に渡している。第2引数はfloatに変換され、次にintにキャストされ、最後にint型の値から減算されている。結果はint型の値として呼び出し元に返される。


文字列の抽出のためのJavaメソッドは何ですか

精度の低下が原因で、このメソッドは予期せぬ計算結果を返すかもしれない。FP-strict モードでは、float型の値は仮数部23ビット、符号1ビット、指数部8ビットを持つ。FP-strict モードの詳細については「NUM06-J. どのプラットフォームでも一貫した浮動小数点数演算を行うために strictfp 修飾子を使う」を参照。この指数部によってfloat型はint型よりも広い範囲の値を表現することができる。しかし、仮数部が23ビットであるということは、floatは23ビット以内で表現できる整数のみを正確に表現できるということを意味する。言い換えると、その範囲外の整数の表現は近似値になるということである。

 strictfp class WideSample {   public static int subFloatFromInt(int op1, float op2) {     return op1 - (int)op2;   }     public static void main(String[] args) {     int result = subFloatFromInt(1234567890, 1234567890);     // 想定される 0 ではなく、-46 を出力する     System.out.println(result);    }   } 

longからfloatもしくはdoubleへの変換でも同様の精度低下が発生しうる。

適合コード (ArithmeticException)

以下の解決法では、整数引数(op1)の範囲検査を行い、精度の低下を引き起こすことなくfloat型の値として表現できることを保証している。


Javaスクリプトの配列を処理する方法
 strictfp class WideSample {   public static int subFloatFromInt(int op1, float op2)                      throws ArithmeticException {       // 仮数部は最大23ビット保持できる     if ((op2 > 0x007fffff) || (op2 < -0x800000)) {       throw new ArithmeticException("Insufficient precision");      }       return op1 - (int)op2;   }     public static void main(String[] args) {     int result = subFloatFromInt(1234567890, 1234567890);     System.out.println(result);    } } 

この例では、subFloatFromInt()メソッドは例外ArithmeticExceptionをスローする。longからfloatdoubleへの変換を行う場合、このような値の範囲検査を行う方法をとることもできる。

適合コード (より大きな型を使う)

以下の適合コードでは、float型ではなくdouble型の引数を受け取る。FP-strict モードでは、double型の値は、仮数部52ビット、符号1ビット、指数部11ビットをとる。int型およびそれより小さな型の整数値は、精度の低下を伴うことなくdoubleへ変換することができる。

 strictfp class WideSample {   public static int subDoubleFromInt(int op1, double op2) {     return op1 - (int)op2;   }     public static void main(String[] args) {     int result = subDoubleFromInt(1234567890, 1234567890);     // 期待通りの動作をする     System.out.println(result);    } } 

プリミティブ整数の型がlongである場合、この解決法は使えないことに注意。Javaにはlong型のすべての範囲を表現できる仮数部を持つような浮動小数点数型は存在しない。


例外

NUM13-EX0: 最下位ビットの欠損が許容できることが数値解析上わかる場合、範囲検査を行わずに整数型から浮動小数点数型へ変換してもよい。

リスク評価

整数値をそれより仮数部のビット数の少ない浮動小数点数に変換すると、丸めエラーを引き起こすことがある。

ルール 深刻度 可能性 修正コスト 優先度 レベル
NUM13-J P2 L3
自動検出

精度の低下を招くキャストを自動検出するのは容易である。そのようなキャストが、プログラマが意図したものかどうかを判断するのは一般に不可能である。ヒューリスティックな警告は役に立つであろう。

関連ガイドライン
The CERT C Secure Coding Standard FLP36-C. Beware of precision loss when converting integral types to floating point
The CERT C++ Secure Coding Standard FLP36-CPP. Beware of precision loss when converting integral types to floating point
参考文献
[JLS 2005] §5.1.2, "Widening Primitive Conversion"
翻訳元

これは以下のページを翻訳したものです。

NUM13-J. Avoid loss of precision when converting primitive integers to floating-point (revision 116)

Top へ



These are our most popular posts:

Java言語規定 型,値及び変数

数値型は,整数的な型である byte , short , int , long 及び char ,並びに浮動小数点 型である float 及び double に分ける。参照型(4.3)は,クラス ... 空型には名前がない ので,空型の変数を宣言すること又は空型にキャストすることはできない。空型の式が 取りうる唯一の .... とする。特に,Javaは,IEEE 754の 非正規化(denormalized) 浮動 小数点数及び緩やかなアンダフロー(gradual underflow) を扱う必要がある。これらは, 数値 ... read more

強く型付けされているJavaの理解に必修の"型変換" (3/3) - @IT

2010年9月9日 ... 扱える値がより広いデータ型へ変換するような処理は、「広くする基本データ型変換( widening primitive)」といわれます。こういった変換 ... 浮動小数点数値が整数に キャストされる場合は、小数点数部分は0に近い方に丸められて失われます。 read more

キャストとは : JavaA2Z

逆に、浮動小数点型から整数型へは明示的にキャストする必要があり、その際、小数点 以下は切り捨てられる。 ... また、Javaでは参照型でもその参照値を直接取得したり 格納することはできないため、任意の整数値を参照型変数にセットしたりといったことは ... read more

Javaの理論と実践: ポイントは何処ですか?

2003年1月1日 ... この記事では、Javaプログラムで非整数型を扱うときに直面する落とし穴について説明 します。 ... 指数表現で任意の数値を表す方法はいくつもあるため、浮動小数点数は、 小数点の左側を1とする基数2の小数で表現するように正規化されています。この形式に .... これは、結果が整数値になるように「見える」演算でも、整数的な型へキャストすること によって整数以外の部分が破棄されてしまうためです。次の例をご覧 ... read more

0 件のコメント:

コメントを投稿