浮動小数点数の2進数(または16進数)表現を表示したいと思います。私は(ここの方法を使用して)手動で変換する方法を知っていますが、同じことを行うコードサンプルを見ることに興味があります。
私はC++とJavaのソリューションに特に興味がありますが、どの言語でも特に簡単にできるのではないかと思うので、この言語に依存しないようにしています。他の言語でいくつかの解決策を見たいです。
編集:私はC、C ++、C#、およびJavaを十分にカバーしています。リストに追加したい代替言語の達人はいますか?
浮動小数点数の2進数(または16進数)表現を表示したいと思います。私は(ここの方法を使用して)手動で変換する方法を知っていますが、同じことを行うコードサンプルを見ることに興味があります。
私はC++とJavaのソリューションに特に興味がありますが、どの言語でも特に簡単にできるのではないかと思うので、この言語に依存しないようにしています。他の言語でいくつかの解決策を見たいです。
編集:私はC、C ++、C#、およびJavaを十分にカバーしています。リストに追加したい代替言語の達人はいますか?
C /C++は簡単です。
union ufloat {
float f;
unsigned u;
};
ufloat u1;
u1.f = 0.3f;
次に、を出力しますu1.u。この実装を適応させることができます。
同様に簡単に2倍になります。
union udouble {
double d;
unsigned long u;
}
doubleは64ビットだからです。
Javaは少し簡単です。Float.floatToRawIntBits()をInteger.toBinaryString()と組み合わせて使用し、Double.doubleToRawLongBitsをLong.toBinaryString ( )と組み合わせて使用します。
C:
int fl = *(int*)&floatVar;
&floatVarアドレス メモリを取得し、次に(int*)このアドレス メモリへのポインタになり、最後に * で 4 バイト float の値を int で取得します。次に、バイナリ形式または16進数形式を印刷できます。
Java:グーグル検索はSunのフォーラムでこのリンクを見つけます
具体的には(私はこれを自分で試していません)
long binary = Double.doubleToLongBits(3.14159);
String strBinary = Long.toBinaryString(binary);
.NET (C# を含む) では、BitConverter多くの型を受け入れるため、生のバイナリにアクセスできます。16進数を取得するにToString("x2")は、最も一般的なオプションです(おそらくユーティリティメソッドにラップされています):
byte[] raw = BitConverter.GetBytes(123.45);
StringBuilder sb = new StringBuilder(raw.Length * 2);
foreach (byte b in raw)
{
sb.Append(b.ToString("x2"));
}
Console.WriteLine(sb);
奇妙なことに、base-64 には 1 行の変換 ( Convert.ToBase64String) がありますが、base-16 にはより多くの労力がかかります。Microsoft.VisualBasic を参照しない限り、その場合は次のようになります。
long tmp = BitConverter.DoubleToInt64Bits(123.45);
string hex = Microsoft.VisualBasic.Conversion.Hex(tmp);
私はこのようにしました:
/*
@(#)File: $RCSfile: dumpdblflt.c,v $
@(#)Version: $Revision: 1.1 $
@(#)Last changed: $Date: 2007/09/05 22:23:33 $
@(#)Purpose: Print C double and float data in bytes etc.
@(#)Author: J Leffler
@(#)Copyright: (C) JLSS 2007
@(#)Product: :PRODUCT:
*/
/*TABSTOP=4*/
#include <stdio.h>
#include "imageprt.h"
#ifndef lint
/* Prevent over-aggressive optimizers from eliminating ID string */
extern const char jlss_id_dumpdblflt_c[];
const char jlss_id_dumpdblflt_c[] = "@(#)$Id: dumpdblflt.c,v 1.1 2007/09/05 22:23:33 jleffler Exp $";
#endif /* lint */
union u_double
{
double dbl;
char data[sizeof(double)];
};
union u_float
{
float flt;
char data[sizeof(float)];
};
static void dump_float(union u_float f)
{
int exp;
long mant;
printf("32-bit float: sign: %d, ", (f.data[0] & 0x80) >> 7);
exp = ((f.data[0] & 0x7F) << 1) | ((f.data[1] & 0x80) >> 7);
printf("expt: %4d (unbiassed %5d), ", exp, exp - 127);
mant = ((((f.data[1] & 0x7F) << 8) | (f.data[2] & 0xFF)) << 8) | (f.data[3] & 0xFF);
printf("mant: %16ld (0x%06lX)\n", mant, mant);
}
static void dump_double(union u_double d)
{
int exp;
long long mant;
printf("64-bit float: sign: %d, ", (d.data[0] & 0x80) >> 7);
exp = ((d.data[0] & 0x7F) << 4) | ((d.data[1] & 0xF0) >> 4);
printf("expt: %4d (unbiassed %5d), ", exp, exp - 1023);
mant = ((((d.data[1] & 0x0F) << 8) | (d.data[2] & 0xFF)) << 8) |
(d.data[3] & 0xFF);
mant = (mant << 32) | ((((((d.data[4] & 0xFF) << 8) |
(d.data[5] & 0xFF)) << 8) | (d.data[6] & 0xFF)) << 8) |
(d.data[7] & 0xFF);
printf("mant: %16lld (0x%013llX)\n", mant, mant);
}
static void print_value(double v)
{
union u_double d;
union u_float f;
f.flt = v;
d.dbl = v;
printf("SPARC: float/double of %g\n", v);
image_print(stdout, 0, f.data, sizeof(f.data));
image_print(stdout, 0, d.data, sizeof(d.data));
dump_float(f);
dump_double(d);
}
int main(void)
{
print_value(+1.0);
print_value(+2.0);
print_value(+3.0);
print_value( 0.0);
print_value(-3.0);
print_value(+3.1415926535897932);
print_value(+1e126);
return(0);
}
SUN UltraSPARC で実行すると、次の結果が得られました。
SPARC: float/double of 1
0x0000: 3F 80 00 00 ?...
0x0000: 3F F0 00 00 00 00 00 00 ?.......
32-bit float: sign: 0, expt: 127 (unbiassed 0), mant: 0 (0x000000)
64-bit float: sign: 0, expt: 1023 (unbiassed 0), mant: 0 (0x0000000000000)
SPARC: float/double of 2
0x0000: 40 00 00 00 @...
0x0000: 40 00 00 00 00 00 00 00 @.......
32-bit float: sign: 0, expt: 128 (unbiassed 1), mant: 0 (0x000000)
64-bit float: sign: 0, expt: 1024 (unbiassed 1), mant: 0 (0x0000000000000)
SPARC: float/double of 3
0x0000: 40 40 00 00 @@..
0x0000: 40 08 00 00 00 00 00 00 @.......
32-bit float: sign: 0, expt: 128 (unbiassed 1), mant: 4194304 (0x400000)
64-bit float: sign: 0, expt: 1024 (unbiassed 1), mant: 2251799813685248 (0x8000000000000)
SPARC: float/double of 0
0x0000: 00 00 00 00 ....
0x0000: 00 00 00 00 00 00 00 00 ........
32-bit float: sign: 0, expt: 0 (unbiassed -127), mant: 0 (0x000000)
64-bit float: sign: 0, expt: 0 (unbiassed -1023), mant: 0 (0x0000000000000)
SPARC: float/double of -3
0x0000: C0 40 00 00 .@..
0x0000: C0 08 00 00 00 00 00 00 ........
32-bit float: sign: 1, expt: 128 (unbiassed 1), mant: 4194304 (0x400000)
64-bit float: sign: 1, expt: 1024 (unbiassed 1), mant: 2251799813685248 (0x8000000000000)
SPARC: float/double of 3.14159
0x0000: 40 49 0F DB @I..
0x0000: 40 09 21 FB 54 44 2D 18 @.!.TD-.
32-bit float: sign: 0, expt: 128 (unbiassed 1), mant: 4788187 (0x490FDB)
64-bit float: sign: 0, expt: 1024 (unbiassed 1), mant: 2570638124657944 (0x921FB54442D18)
SPARC: float/double of 1e+126
0x0000: 7F 80 00 00 ....
0x0000: 5A 17 A2 EC C4 14 A0 3F Z......?
32-bit float: sign: 0, expt: 255 (unbiassed 128), mant: 0 (0x000000)
64-bit float: sign: 0, expt: 1441 (unbiassed 418), mant: -1005281217 (0xFFFFFFFFC414A03F)
私はしばらくここに投稿することを考えなければなりませんでした。なぜなら、これは仲間のコーダーが C で悪いことをするように刺激する可能性があるからです。とにかく投稿することにしましたが、覚えておいてください。三度考えます。
免責事項はさておき、ここに行きます。
最初に、たとえば符号なしの long 変数をバイナリ形式で出力する関数を作成します。
void printbin(unsigned long x, int n)
{
if (--n) printbin(x>>1, n);
putchar("01"[x&1]);
}
残念ながら、この関数を直接使用して float 変数を出力することはできないため、少しハックする必要があります。このハックは、 Carmack の Quake に対する逆平方根のトリックについて読んだことがある人なら誰でも知っているように見えるでしょう。アイデアは、float 変数に値を設定してから、long 整数変数に同じビット マスクを取得するというものです。したがって、f のメモリ アドレスを取得して long* 値に変換し、そのポインタを使用して f のビット マスクを long unsigned として取得します。この値を long unsigned として出力すると、結果は混乱しますが、ビットは元の float 値と同じであるため、実際には問題になりません。
int main(void)
{
long unsigned lu;
float f = -1.1f;
lu = *(long*)&f;
printbin(lu, 32);
printf("\n");
return 0;
}
この構文がひどいと思うなら、あなたは正しいです。
Haskell では、アクセス可能な浮動小数点の内部表現はありません。ただし、Float や Double など、多くの形式からバイナリ シリアライズを行うことができます。次のソリューションは、Data.Binary サポートのインスタンスを持つすべての型に一般的です。
module BinarySerial where
import Data.Bits
import Data.Binary
import qualified Data.ByteString.Lazy as B
elemToBits :: (Bits a) => a -> [Bool]
elemToBits a = map (testBit a) [0..7]
listToBits :: (Bits a) => [a] -> [Bool]
listToBits a = reverse $ concat $ map elemToBits a
rawBits :: (Binary a) => a -> [Bool]
rawBits a = listToBits $ B.unpack $ encode a
変換は rawBits で行うことができます:
rawBits (3.14::Float)
しかし、この方法で float 値にアクセスする必要がある場合は、おそらく何か問題があります。本当の質問は、浮動小数点数の指数と仮数にアクセスする方法ですか? 答えは Prelude の指数と仮数です。
significand 3.14
0.785
exponent 3.14
2
まあ、(Java の) Float クラスと Double クラスの両方に toHexString('float') メソッドがあるので、16 進変換にはほぼこれで十分です。
Double.toHexString(42344);
Float.toHexString(42344);
やさしい!
パイソン:
コード:
import struct
def float2bin(number, hexdecimal=False, single=False):
bytes = struct.pack('>f' if single else '>d', number)
func, length = (hex, 2) if hexdecimal else (bin, 8)
byte2bin = lambda byte: func(ord(byte))[2:].rjust(length, '0')
return ''.join(map(byte2bin, bytes))
サンプル:
>>> float2bin(1.0)
'0011111111110000000000000000000000000000000000000000000000000000'
>>> float2bin(-1.0)
'1011111111110000000000000000000000000000000000000000000000000000'
>>> float2bin(1.0, True)
'3ff0000000000000'
>>> float2bin(1.0, True, True)
'3f800000'
>>> float2bin(-1.0, True)
'bff0000000000000'
C# でこのようなコードを使用すると、float 変数を int 変数 (または double から long) に簡単に変換できます。
float f = ...;
unsafe
{
int i = *(int*)&f;
}
C ++では、次のようにバイナリ表現を表示できます。
template <class T>
std::bitset<sizeof(T)*8> binary_representation(const T& f)
{
typedef unsigned long TempType;
assert(sizeof(T)<=sizeof(TempType));
return std::bitset<sizeof(T)*8>(*(reinterpret_cast<const TempType*>(&f)));
}
ここでの制限は、bitsetlongパラメーターがunsignedlongであるという事実によるものです。したがって、floatまで機能し、bitsetとassentをアサートする以外のものを使用できます。
ところで、cletusの提案は、doubleをカバーするために「unsingedlong long」が必要であるという意味で失敗します。とにかく、バイナリ(1または0)表現を示すものが必要です。