floatを長さ4のバイト配列(char *の配列)に変換する方法は?ネットワーク経由でデータtcpを送信する必要があり、floatをバイト配列として送信する必要があります。(私は小数点以下2桁の精度を知っているので、現時点では、クライアント側で100を掛け、サーバーで100で割ります-基本的に整数に変換してから、&0xff <<操作でバイトを見つけます)。しかし、それは醜く、時間の経過とともに精度を失う可能性があります。
3 に答える
任意のタイプをバイトのシーケンスとして読み取るのは非常に簡単です。
float f = 0.5f;
unsigned char const * p = reinterpret_cast<unsigned char const *>(&f);
for (std::size_t i = 0; i != sizeof(float); ++i)
{
std::printf("The byte #%zu is 0x%02X\n", i, p[i]);
}
ネットワークストリームからフロートへの書き込みconst
も同様に機能しますが、を省略した場合のみです。
任意のオブジェクトをバイトのシーケンスとして再解釈することは常に許可されており(任意のchar
タイプが許可されます)、これは明らかにエイリアシング違反ではありません。もちろん、どのタイプのバイナリ表現もプラットフォームに依存するため、受信者が同じプラットフォームを使用している場合にのみ、シリアル化にこれを使用する必要があることに注意してください。
最初に行う必要があるのは、ネットワーク プロトコルで float の形式を決定することです。それが 4 バイトであることを知っているだけでは、あまりわかりません。IBM メインフレーム、Oracle Sparc、および通常の PC はすべて 4 バイト浮動小数点数ですが、3 つの異なる形式があります。形式がわかったら、その形式と移植性の要件に応じて、次の 2 つの異なる戦略を使用できます。
プロトコルの形式が IEEE (最も一般的なケース) であり、IEEE ではないマシンに移植可能である必要がない場合 (Windows とほとんどの Unix は IEEE であり、ほとんどのメインフレームはそうではありません)、次を使用できます。 punning と入力して float を に変換し、次のuint32_t
いずれかを使用して出力します。
std::ostream&
output32BitUInt( std::ostream& dest, uint32_t value )
{
dest.put( (value >> 24) & 0xFF );
dest.put( (value >> 16) & 0xFF );
dest.put( (value >> 8) & 0xFF );
dest.put( (value ) & 0xFF );
}
ビッグエンディアン (通常のネットワーク順序) の場合、または:
std::ostream&
output32BitUInt( std::ostream& dest, uint32_t value )
{
dest.put( (value ) & 0xFF );
dest.put( (value >> 8) & 0xFF );
dest.put( (value >> 16) & 0xFF );
dest.put( (value >> 24) & 0xFF );
}
リトルエンディアン用 (一部のプロトコルで使用)。どちらを使用するかは、プロトコルに定義されている形式によって異なります。
float
からに変換するにuint32_t
は、コンパイラを確認する必要があります。を使用memcpy
することは、標準によって完全に保証されている唯一の方法です。その意図はreinterpret_cast<uint32_t&>
、float での の使用も同様に機能し、ほとんどの (すべて?) コンパイラも の使用をサポートすることunion
です。
メインフレームにも移植可能にする必要がある場合、または形式が IEEE 以外の場合は、float から指数、符号、および仮数を抽出し、それぞれをターゲット形式で出力する必要があります。次のようなものは、任意のマシン (IEEE を使用しないメインフレームを含む) で IEEE ビッグエンディアンを出力するために機能し、いくつかのアイデアを提供するはずです。
oxdrstream&
oxdrstream::operator<<(
float source )
{
BytePutter dest( *this ) ;
bool isNeg = source < 0 ;
if ( isNeg ) {
source = - source ;
}
int exp ;
if ( source == 0.0 ) {
exp = 0 ;
} else {
source = ldexp( frexp( source, &exp ), 24 ) ;
exp += 126 ;
}
uint32_t mant = source ;
dest.put( (isNeg ? 0x80 : 0x00) | exp >> 1 ) ;
dest.put( ((exp << 7) & 0x80) | ((mant >> 16) & 0x7F) ) ;
dest.put( mant >> 8 ) ;
dest.put( mant ) ;
return *this ;
}
(BytePutter
は、通常のボイラープレートを処理し、エラー チェックを行う単純なクラスです。) もちろん、出力形式が IEEE でない場合、出力のさまざまな操作は異なりますが、これは基本的な原則を示しているはずです。( をサポートしていないよりエキゾチックなメインフレームへの移植性が必要な場合はuint32_t
、23 ビットより大きい任意の符号なし整数型に置き換えることができます。)
C言語の1つの領域にデータを重ねるだけ
union dataUnion {
float f;
char fBuff[sizeof(float)];
}
// to use:
dataUnion myUnion;
//
myUnion.f = 3.24;
for(int i=0;i<sizeof(float);i++)
fputc(myUnion.fbuff[i],fp); // or what ever stream output....