「実数値」から「float値の整数表現」を求める方法を調べてみた

float値は内部的には32個のビットで表現されます。Float.intBitsToFloat()を使うと、整数値で表現したビットに対応するfloat値を取得することができます。この整数表現はint型で行います。
たとえば、int型の整数値「0xC0599999(十六進数)」をFloat.intBitsToFloat()に渡すと、float値「-3.4」を取得できます。
今回は、実数値からfloat値の整数表現を求める方法を調べました。

「実数値 ⇒ 整数表現」を図解してみた

実数値から対応する整数表現を求める流れを図解してみました。この図解では、実数「-3.4」からfloat値の整数表現(十六進数)「0xC0599999」を求めています。

図解からコードを書いてみた

図解の中の○に当たる処理を、項番順に書いてみます。それぞれ処理を行うコードに続けて、各処理の結果を書いています。

スタート
BigDecimal real = new BigDecimal( "-3.4" );
System.out.println( "実数: " + real );

実数: -3.4


1.絶対値を求める。
BigDecimal absolute = real.abs();
System.out.println( "絶対値: " + absolute );

絶対値: 3.4


2.仮数・指数を求める。
SignificandAndExponent sae = toSignificandAndExponent( absolute );
BigDecimal significand = sae.significand;
BigInteger exponent = sae.exponent;
System.out.println( "仮数: " + significand );
System.out.println( "指数: " + exponent );
.
.
// [構造体] 仮数と指数
private static class SignificandAndExponent {
	BigDecimal significand;	// 仮数
	BigInteger exponent;	// 指数
}

private static SignificandAndExponent toSignificandAndExponent( BigDecimal absolute ) {

	// BigInteger
	final BigInteger ZERO_INT = new BigInteger( "0" );	// 0
	final BigInteger ONE_INT = new BigInteger( "1" );	// 1
	final BigInteger TWO_INT = new BigInteger( "2" );	// 2
	
	// BigDecimal
	final BigDecimal TWO_DEC = new BigDecimal( "2" );	// 2
	
	BigDecimal significand = absolute;	// 仮数
	BigInteger exponent = ZERO_INT;		// 指数
	
	// 仮数が2より大きい間繰り返す。
	while ( significand.compareTo( TWO_DEC ) > 0 ) {
		int scale = significand.scale();	// 計算結果のスケール

		// 仮数を2で割った時に、桁が増えないことを確認する。
		// ※仮数の整数部分が奇数の場合、桁が増える。
		if ( ONE_INT.equals( significand.unscaledValue().mod( TWO_INT ) ) ) {
			// 桁が増える:
			
			// 計算結果のスケールを1増やす。
			++scale;
		}
		
		// 仮数を2で割る。
		// ※丸目が不要なように計算結果のスケールを指定している。
		significand = significand.divide( TWO_DEC, scale, BigDecimal.ROUND_UNNECESSARY );
		// 指数を1増やす。
		exponent = exponent.add( BigInteger.ONE );
	}
	
	// 戻り値の構造体を作成する。
	SignificandAndExponent bak = new SignificandAndExponent();
	bak.significand = significand;	// 仮数
	bak.exponent = exponent;		// 指数
	
	return bak;
}

仮数: 1.7
指数: 1


3.仮数を補正する。
significand = adjustSignificand( significand );
System.out.println( "仮数: " + significand );
.
.
private static BigDecimal adjustSignificand( BigDecimal significand ) {
	// 仮数から1を引く。
	return significand.subtract( new BigDecimal( 1 ) );
}

仮数: 0.7


4.仮数を二進数にする。
String significandBin = significandToBinary( significand );
System.out.println( "仮数(二進数) : " + significandBin );
.
.
private static String significandToBinary( BigDecimal significand ) {
	final BigDecimal ONE = new BigDecimal( 1 );	// 1
	final BigDecimal TWO = new BigDecimal( 2 );	// 2
	
	StringBuilder bak = new StringBuilder();
	
	do {
		// 仮数を二倍する。
		BigDecimal twice = significand.multiply( TWO );

		// 仮数の二倍が0より小さいことを確認する。
		if ( twice.compareTo( ONE ) < 0 ) {
			// 結果の末尾に0を加える。
			bak.append( "0" );
			// 仮数 = 仮数 × 2
			significand = twice;
		} else {
			// 仮数の二倍が0以上である:
			
			// 結果の末尾に1を加える。
			bak.append( "1" );
			// 仮数 = 仮数 × 2 - 1
			significand = twice.subtract( ONE );
		}
		
	// 結果の長さが仮数の有効桁に達するまで繰り返す。
	} while ( bak.length() < N );
	
	return bak.toString();
}

仮数(二進数) : 10110011001100110011001


5.指数を補正する。
exponent = adjustExponent( exponent );
System.out.println( "指数: " + exponent );
.
.
private static BigInteger adjustExponent( BigInteger exponent ) {
	return exponent.add( E ); 
}

指数: 128


6.指数を二進数にする。
String exponentBin = exponentToBinary( exponent );
System.out.println( "指数(二進数) : " + exponentBin );
.
.
private static String exponentToBinary( BigInteger exponent ) {
	return exponent.toString( 2 );
}

指数(二進数) : 10000000


7.符号を求める。
int sign = real.signum();
System.out.println( "符号 : " + sign );

符号 : -1


8.符号を二進数にする。
String signBin = signToBinary( sign );
System.out.println( "符号(二進数) : " + signBin );
.
.
private static String signToBinary( int sign ) {
	if ( sign < 0 ) {
		return "1";
	} else {
		return "0";
	}
}

符号(二進数) : 1

9.二進数を連結する。


String binary = signBin + exponentBin + significandBin;
System.out.println( "float値の整数表現(二進数): " + binary );

float値の整数表現(二進数): 11000000010110011001100110011001


10.二進数を十六進数にする
String hex = binaryToHex( binary );
System.out.println( "float値の整数表現(十六進数): " + hex );
.
.
private static String binaryToHex( String binary ) {
	StringBuffer bak = new StringBuffer();
	
	for ( int i = 0; i < binary.length(); i += 4 ) {
		String substr = binary.substring( i, i + 4 );
		int n = Integer.parseInt( substr, 2 );
		bak.append( Integer.toHexString( n ) );
	}
	
	return bak.toString();
}

float値の整数表現(十六進数): c0599999

求めた整数表現をFloat.intBitsToFloat()に渡してみた

実数値「-3.4」から求めた整数表現(十六進数)「c0599999」を、Float.intBitsToFloat()に渡してみました。

float f = Float.intBitsToFloat( 0xc0599999 );
System.out.println( "float値:" + f );

float値:-3.3999999

「-3.3999999」≒「-3.4」なのでOKです!
floatなので誤差が出ることがあります。

コードをすべて載せてみる

実数値から整数表現を求めるクラスのコードをすべて載せます。
ちなみに、float型ではなく、double型の整数表現を求めることもできます。このコードの最初の方に「floatの場合」と「doubleの場合」というコメントがあります。double型の整数表現を求めるには、「floatの場合」に続く定数宣言をコメント化し、代わりに「doubleの場合」に続く定数宣言のコメントをはずします。

import java.math.BigDecimal;
import java.math.BigInteger;

public class RealToIntBits {
	
	// floatの場合:
	private static final BigInteger E = new BigInteger( "127" );	// 指数の補正値
	private static final int K = 8;	// 指数の桁数
	private static final int N = 23;	// 仮数の桁数

	// doubleの場合:
	// private static BigInteger E = new BigInteger( "1023" );	// 指数の補正値
	// private static int K = 11;	// 指数の桁数
	// private static int N = 52;	// 仮数の桁数


	// [構造体] 仮数と指数
	private static class SignificandAndExponent {
		BigDecimal significand;	// 仮数
		BigInteger exponent;	// 指数
	}
	
	/**
	 * 実数からfloat値の整数表現(十六進数)を求める。
	 */
	public static void main( String[] args ) {
		
		BigDecimal real = new BigDecimal( "-3.4" );

		System.out.println( "実数: " + real );
		System.out.println();
		
		// 1.絶対値を求める。
		System.out.println( "1.絶対値を求める。");
		BigDecimal absolute = real.abs();

		System.out.println( "絶対値: " + absolute );
		System.out.println();
		
		// 2.仮数・指数を求める。
		System.out.println( "2.仮数・指数を求める。" );
		SignificandAndExponent sae = toSignificandAndExponent( absolute );
		BigDecimal significand = sae.significand;
		BigInteger exponent = sae.exponent;

		System.out.println( "仮数: " + significand );
		System.out.println( "指数: " + exponent );
		System.out.println();
		
		// 3.仮数を補正する。
		System.out.println( "3.仮数を補正する。" );
		significand = adjustSignificand( significand );

		System.out.println( "仮数: " + significand );
		System.out.println();
		
		// 4.仮数を二進数にする。
		System.out.println( "4.仮数を二進数にする。" );
		String significandBin = significandToBinary( significand );

		System.out.println( "仮数(二進数) : " + significandBin );
		System.out.println();
		
		// 5.指数を補正する。
		System.out.println( "5.指数を補正する。" );
		exponent = adjustExponent( exponent );

		System.out.println( "指数: " + exponent );
		System.out.println();
		
		// 6.指数を二進数にする。
		System.out.println( "6.指数を二進数にする。" );
		String exponentBin = exponentToBinary( exponent );

		System.out.println( "指数(二進数) : " + exponentBin );
		System.out.println();

		// 7.符号を求める。
		System.out.println( "7.符号を求める。" );
		int sign = real.signum();

		System.out.println( "符号 : " + sign );
		System.out.println();
		
		// 8.符号を二進数にする。
		System.out.println( "8.符号を二進数にする。" );
		String signBin = signToBinary( sign );

		System.out.println( "符号(二進数) : " + signBin );
		System.out.println();
		
		// 9.二進数を連結する。
		System.out.println( "9.二進数を連結する。" );
		String binary = signBin + exponentBin + significandBin;

		System.out.println( "float値の整数表現(二進数): " + binary );
		System.out.println();
		
		// 10.二進数を十六進数にする
		System.out.println( "10.二進数を十六進数にする" );
		String hex = binaryToHex( binary );

		System.out.println( "float値の整数表現(十六進数): " + hex );
		System.out.println();
	}
	
	
	// 2.仮数・指数を求める。
	private static SignificandAndExponent toSignificandAndExponent( BigDecimal absolute ) {

		// BigInteger
		final BigInteger ZERO_INT = new BigInteger( "0" );	// 0
		final BigInteger ONE_INT = new BigInteger( "1" );	// 1
		final BigInteger TWO_INT = new BigInteger( "2" );	// 2
		
		// BigDecimal
		final BigDecimal TWO_DEC = new BigDecimal( "2" );	// 2
		
		BigDecimal significand = absolute;	// 仮数
		BigInteger exponent = ZERO_INT;		// 指数
		
		// 仮数が2より大きい間繰り返す。
		while ( significand.compareTo( TWO_DEC ) > 0 ) {
			int scale = significand.scale();	// 計算結果のスケール

			// 仮数を2で割った時に、桁が増えないことを確認する。
			// ※仮数の整数部分が奇数の場合、桁が増える。
			if ( ONE_INT.equals( significand.unscaledValue().mod( TWO_INT ) ) ) {
				// 桁が増える:
				
				// 計算結果のスケールを1増やす。
				++scale;
			}
			
			// 仮数を2で割る。
			// ※丸目が不要なように計算結果のスケールを指定している。
			significand = significand.divide( TWO_DEC, scale, BigDecimal.ROUND_UNNECESSARY );
			// 指数を1増やす。
			exponent = exponent.add( BigInteger.ONE );
		}
		
		// 戻り値の構造体を作成する。
		SignificandAndExponent bak = new SignificandAndExponent();
		bak.significand = significand;	// 仮数
		bak.exponent = exponent;		// 指数
		
		return bak;
	}
	
	// 3.仮数を補正する。
	private static BigDecimal adjustSignificand( BigDecimal significand ) {
		// 仮数から1を引く。
		return significand.subtract( new BigDecimal( 1 ) );
	}
	
	// 4.仮数を二進数にする。
	private static String significandToBinary( BigDecimal significand ) {
		final BigDecimal ONE = new BigDecimal( 1 );	// 1
		final BigDecimal TWO = new BigDecimal( 2 );	// 2
		
		StringBuilder bak = new StringBuilder();
		
		do {
			// 仮数を二倍する。
			BigDecimal twice = significand.multiply( TWO );

			// 仮数の二倍が0より小さいことを確認する。
			if ( twice.compareTo( ONE ) < 0 ) {
				// 結果の末尾に0を加える。
				bak.append( "0" );
				// 仮数 = 仮数 × 2
				significand = twice;
			} else {
				// 仮数の二倍が0以上である:
				
				// 結果の末尾に1を加える。
				bak.append( "1" );
				// 仮数 = 仮数 × 2 - 1
				significand = twice.subtract( ONE );
			}
			
		// 結果の長さが仮数の有効桁に達するまで繰り返す。
		} while ( bak.length() < N );
		
		return bak.toString();
	}
	
	// 5.指数を補正する。
	private static BigInteger adjustExponent( BigInteger exponent ) {
		return exponent.add( E ); 
	}
	
	// 6.指数を二進数にする。
	private static String exponentToBinary( BigInteger exponent ) {
		return exponent.toString( 2 );
	}

	// 8.符号を二進数にする。
	private static String signToBinary( int sign ) {
		if ( sign < 0 ) {
			return "1";
		} else {
			return "0";
		}
	}
	
	// 10.二進数を十六進数にする
	private static String binaryToHex( String binary ) {
		StringBuffer bak = new StringBuffer();
		
		for ( int i = 0; i < binary.length(); i += 4 ) {
			String substr = binary.substring( i, i + 4 );
			int n = Integer.parseInt( substr, 2 );
			bak.append( Integer.toHexString( n ) );
		}
		
		return bak.toString();
	}
}