桁落ち
日本語 | 桁落ち |
英語 | underflow |
ふりがな | けたおち |
フリガナ | ケタオチ |
浮動小数点の計算において、正しい値を持つ桁が減ること。
指数部が大きく変わるような計算を行った場合に、それに伴い仮数部が表現していた桁が移動し、これまで存在していなかった桁に0が埋められ、それによりある桁以降に不正な値が現れることを「桁落ち」という。「アンダーフロー」とも言う。
たとえば「2.00000001 - 2.0 = 0.00000001」の計算を行う場合。
「2.00000001」が「0.00000001」へと変化した時、仮数部で表現する値は「2.00000001」から「1」に変化する。「0.00000001」の「0.0000000」の範囲は全て0のため仮数部ではなく指数部で「小数部8桁」という形で表現される。
仮数部では「2.0000000」の範囲がなくなったため、仮数部の計算結果はビット的に左にずれる。ずれた結果、空いた右側のビットには0が埋められる。
だが、この0は正しい値ではない。「2.00000001」を表す仮数部のビット表現は、本当であれば無限に続くビットで表現するべきもので、それをやむなく仮数部のサイズで切り落としているだけだからである。
結果、0で埋められた範囲の桁は「誤った値を持つ桁」となり、正しい桁を持つ範囲が狭まることになる。つまり「桁が減る」ことになり、これを「桁落ち」という。
そもそもは数学用語。数学や物理計算では「小数点第3位で四捨五入」といった形で、有効桁数を制限する。
たとえば、円周率「3.14」の有効桁数は小数点第2位である。つまり小数点第3位以下は「桁落ち」していることになり、この桁より下の値は「0が続く」のではなく「未知の不正な値」である。この値に「0.001」を足した結果が「3.141」というのは誤りである。「3.14」に演算する値も有効桁数が小数点第2位の値でなくてはならず、この例で言えば「0.00」を足して結果「3.14」となる。
Javaの浮動小数点でも、同様に有効桁数を考慮して演算する必要がある。
ただし、浮動小数点は指数部が2の乗数であること、またビット上での処理であることなど分かりづらい面もある。大学の研究等で計算に使用する場合には注意すること。
指数部が大きく変わるような計算を行った場合に、それに伴い仮数部が表現していた桁が移動し、これまで存在していなかった桁に0が埋められ、それによりある桁以降に不正な値が現れることを「桁落ち」という。「アンダーフロー」とも言う。
たとえば「2.00000001 - 2.0 = 0.00000001」の計算を行う場合。
「2.00000001」が「0.00000001」へと変化した時、仮数部で表現する値は「2.00000001」から「1」に変化する。「0.00000001」の「0.0000000」の範囲は全て0のため仮数部ではなく指数部で「小数部8桁」という形で表現される。
仮数部では「2.0000000」の範囲がなくなったため、仮数部の計算結果はビット的に左にずれる。ずれた結果、空いた右側のビットには0が埋められる。
だが、この0は正しい値ではない。「2.00000001」を表す仮数部のビット表現は、本当であれば無限に続くビットで表現するべきもので、それをやむなく仮数部のサイズで切り落としているだけだからである。
結果、0で埋められた範囲の桁は「誤った値を持つ桁」となり、正しい桁を持つ範囲が狭まることになる。つまり「桁が減る」ことになり、これを「桁落ち」という。
そもそもは数学用語。数学や物理計算では「小数点第3位で四捨五入」といった形で、有効桁数を制限する。
たとえば、円周率「3.14」の有効桁数は小数点第2位である。つまり小数点第3位以下は「桁落ち」していることになり、この桁より下の値は「0が続く」のではなく「未知の不正な値」である。この値に「0.001」を足した結果が「3.141」というのは誤りである。「3.14」に演算する値も有効桁数が小数点第2位の値でなくてはならず、この例で言えば「0.00」を足して結果「3.14」となる。
Javaの浮動小数点でも、同様に有効桁数を考慮して演算する必要がある。
ただし、浮動小数点は指数部が2の乗数であること、またビット上での処理であることなど分かりづらい面もある。大学の研究等で計算に使用する場合には注意すること。
参考サイト
- (参考サイトはありません)
// Sample.java
public class Sample
{
public static void main( String[] args )
{
// 2.00000001 - 2.0 = 0.00000001を例に桁落ちを見てみます。
outputDoubleBit( 2.0 );
outputDoubleBit( 2.00000001 );
outputDoubleBit( 2.00000001 - 2.0 );
// 0 10000000000 0000000000000000000000000000000000000000000000000000
// 0 10000000000 0000000000000000000000000001010101111001100011101110
// 0 01111100100 0101011110011000111011100000000000000000000000000000
// 2.00000001のビットと、2.00000001 - 2.0のビットを比較してみます。
// |→ (A) ←|→ (B) ←|
// 2.00000001 : 0 10000000000 0000000000000000000000000001010101111001100011101110
// 2.00000001 - 2.0 : 0 01111100100 0101011110011000111011100000000000000000000000000000
// |→ (B) ←|→ (C) ←|
// 浮動小数点は「指数部」と「仮数部」に分けられます。
// 計算前の2.00000001の場合「2.00000001」全体が仮数部で表現されます。
// 計算結果の0.00000001の場合、「0.0000000」は指数部で、「1」は仮数部で表現されます。
// 頭の「2」がなくなったことで、「2」から最後の「1」までを仮数部で表現する
// 必要が無くなったためです。
// そのため、指数部は「10000000000」から「01111100100」へと大きく減り、
// 仮数部は(A)の箇所がなくなり(B)の箇所がそのまま上へ移動します。
// そうなると、問題は(C)です。(C)は、あたかも(B)の右側に存在していたかのように
// 現れますが、実際には異なりますし、値も異なります。つまり、(C)の箇所は「不正な値」です。
// この場合、この結果で「計算結果として正しいのは(B)の箇所まで」となります。
// つまり「有効な桁が減った」ことになります。これが「桁落ち」です。
}
/**
* double型変数をビット形式で出力します。
*/
private static void outputDoubleBit( double d )
{
// double型変数をビット形式で文字列化します。
String source = Long.toBinaryString( Double.doubleToLongBits( d ) );
// 左0埋めします。
StringBuffer strbuf = new StringBuffer();
for( int iF1 = source.length(); iF1 < 64; ++iF1 )
{
strbuf.append( "0" );
}
strbuf.append( source );
// 符号、仮数部、指数部の間にスペースを入れます。
strbuf.insert( 12, " " );
strbuf.insert( 1, " " );
System.out.println( strbuf.toString() );
}
}
public class Sample
{
public static void main( String[] args )
{
// 2.00000001 - 2.0 = 0.00000001を例に桁落ちを見てみます。
outputDoubleBit( 2.0 );
outputDoubleBit( 2.00000001 );
outputDoubleBit( 2.00000001 - 2.0 );
// 0 10000000000 0000000000000000000000000000000000000000000000000000
// 0 10000000000 0000000000000000000000000001010101111001100011101110
// 0 01111100100 0101011110011000111011100000000000000000000000000000
// 2.00000001のビットと、2.00000001 - 2.0のビットを比較してみます。
// |→ (A) ←|→ (B) ←|
// 2.00000001 : 0 10000000000 0000000000000000000000000001010101111001100011101110
// 2.00000001 - 2.0 : 0 01111100100 0101011110011000111011100000000000000000000000000000
// |→ (B) ←|→ (C) ←|
// 浮動小数点は「指数部」と「仮数部」に分けられます。
// 計算前の2.00000001の場合「2.00000001」全体が仮数部で表現されます。
// 計算結果の0.00000001の場合、「0.0000000」は指数部で、「1」は仮数部で表現されます。
// 頭の「2」がなくなったことで、「2」から最後の「1」までを仮数部で表現する
// 必要が無くなったためです。
// そのため、指数部は「10000000000」から「01111100100」へと大きく減り、
// 仮数部は(A)の箇所がなくなり(B)の箇所がそのまま上へ移動します。
// そうなると、問題は(C)です。(C)は、あたかも(B)の右側に存在していたかのように
// 現れますが、実際には異なりますし、値も異なります。つまり、(C)の箇所は「不正な値」です。
// この場合、この結果で「計算結果として正しいのは(B)の箇所まで」となります。
// つまり「有効な桁が減った」ことになります。これが「桁落ち」です。
}
/**
* double型変数をビット形式で出力します。
*/
private static void outputDoubleBit( double d )
{
// double型変数をビット形式で文字列化します。
String source = Long.toBinaryString( Double.doubleToLongBits( d ) );
// 左0埋めします。
StringBuffer strbuf = new StringBuffer();
for( int iF1 = source.length(); iF1 < 64; ++iF1 )
{
strbuf.append( "0" );
}
strbuf.append( source );
// 符号、仮数部、指数部の間にスペースを入れます。
strbuf.insert( 12, " " );
strbuf.insert( 1, " " );
System.out.println( strbuf.toString() );
}
}
// Sample.java public class Sample { public static void main( String[] args ) { // 2.00000001 - 2.0 = 0.00000001を例に桁落ちを見てみます。 outputDoubleBit( 2.0 ); outputDoubleBit( 2.00000001 ); outputDoubleBit( 2.00000001 - 2.0 ); // 0 10000000000 0000000000000000000000000000000000000000000000000000 // 0 10000000000 0000000000000000000000000001010101111001100011101110 // 0 01111100100 0101011110011000111011100000000000000000000000000000 // 2.00000001のビットと、2.00000001 - 2.0のビットを比較してみます。 // |→ (A) ←|→ (B) ←| // 2.00000001 : 0 10000000000 0000000000000000000000000001010101111001100011101110 // 2.00000001 - 2.0 : 0 01111100100 0101011110011000111011100000000000000000000000000000 // |→ (B) ←|→ (C) ←| // 浮動小数点は「指数部」と「仮数部」に分けられます。 // 計算前の2.00000001の場合「2.00000001」全体が仮数部で表現されます。 // 計算結果の0.00000001の場合、「0.0000000」は指数部で、「1」は仮数部で表現されます。 // 頭の「2」がなくなったことで、「2」から最後の「1」までを仮数部で表現する // 必要が無くなったためです。 // そのため、指数部は「10000000000」から「01111100100」へと大きく減り、 // 仮数部は(A)の箇所がなくなり(B)の箇所がそのまま上へ移動します。 // そうなると、問題は(C)です。(C)は、あたかも(B)の右側に存在していたかのように // 現れますが、実際には異なりますし、値も異なります。つまり、(C)の箇所は「不正な値」です。 // この場合、この結果で「計算結果として正しいのは(B)の箇所まで」となります。 // つまり「有効な桁が減った」ことになります。これが「桁落ち」です。 } /** * double型変数をビット形式で出力します。 */ private static void outputDoubleBit( double d ) { // double型変数をビット形式で文字列化します。 String source = Long.toBinaryString( Double.doubleToLongBits( d ) ); // 左0埋めします。 StringBuffer strbuf = new StringBuffer(); for( int iF1 = source.length(); iF1 < 64; ++iF1 ) { strbuf.append( "0" ); } strbuf.append( source ); // 符号、仮数部、指数部の間にスペースを入れます。 strbuf.insert( 12, " " ); strbuf.insert( 1, " " ); System.out.println( strbuf.toString() ); } }