任意のfloat(32ビット、IEEE形式)の値をprintfのように文字列に変換し、それを逆変換して元のfloatとビット列を完全に一致させることは可能なのでしょうか?
文字列化する際のバッファは何バイト使っても構いません。
原理的に可能なのかと気になっています。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答6件
0
ベストアンサー
floatなら9桁、doubleなら17桁で文字列にすれば、その文字列から元の型に変換したときに同じ値になります。
floatの場合
C
1float num1 = 値; 2char str[20]; 3sprintf(str, "%.9g", num1); 4float num2 = strtof(str, NULL); 5assert(num1 == num2);
doubleの場合
C
1double num1 = 値; 2char str[30]; 3sprintf(str, "%.17g", num1); 4double num2 = strtod(str, NULL); 5assert(num1 == num2);
VC++では float.h にFLT_DECIMAL_DIG
とDBL_DECIMAL_DIG
というマクロでfloatとdoubleの「可逆可能な10進数の桁数」が定義されており、それぞれ、9と17になっています。
ただ、C標準のマクロではないようで、gccでは定義されていませんでした。その代わり、__FLT_DECIMAL_DIG__
と__DBL_DECIMAL_DIG__
が定義済みマクロとして定義されていました。
追記
Wikipediaの IEEE 754 の下の方にある「文字列表現」に書かれていました。VC++のマクロと一致します。
投稿2015/12/11 21:21
編集2015/12/12 01:08総合スコア5944
0
正規化数に限れば10進表現の精度を十分に取れるなら可逆文字列化も可能ですし、+0と-0も"+0","-0"と文字列化していいというルールにすれば表現できますが、NaNや非正規化数が無数のビット表現を持っているのでそれらを一般的な文字列表現(たとえば"Nan")にしてしまうと情報が失われます。
投稿2015/12/11 11:18
総合スコア5570
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/12/11 15:43
退会済みユーザー
2015/12/11 17:11
2015/12/12 21:59
0
逆変換する側が違う種類のマシンを想定するならば、原理的に不可能です(エンディアンの問題を言っている訳ではありません)。
Cは浮動小数点表現について単一の規格(例えばIEEE)を強要していないので、仮数部、基数部...と分けて送ったとしてもそれらのビット数が異なれば、同じビット列になることはありません。
同じマシンを想定するなら10進数の必要はなくバイナリデータで良いと思います。
投稿2015/12/12 00:07
退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/12/12 12:59
退会済みユーザー
2015/12/12 23:18 編集
2015/12/12 23:42
退会済みユーザー
2015/12/13 02:05 編集
2015/12/13 02:31 編集
退会済みユーザー
2015/12/13 06:13
2015/12/13 06:29 編集
退会済みユーザー
2015/12/13 08:59 編集
0
C99以降であればstrtofとprintf系のa/Aを用いることで、16進数表記(バイナリ表記)でやりとりできます。
cppref: strtof, strtod, strtold
cppref: printf, fprintf, sprintf, snprintf, printf_s, fprintf_s
printf系では普通の10進数小数点数表記以外にa
またはA
を使うと16進数表記にすることができます。この表記は標準の桁数がその型が持つ精度を正確に(exact)表現できる桁数(基数が2以外の場合は補足参考)となっており、(桁数を設定しない限り)精度や進数の違いにより情報が落ちることはありません。そして、strtofはその16進数表記をそのまま解釈して変換できます。strtofは-0やinf、nanにも対応しているのでそちらも問題ありません。なお、doubleとlong doubleも同様にできます。
変換を確認できるかも知れないサンプルコード(なぜかC11)
C
1#include <stdio.h> 2#include <stdlib.h> 3 4int main(int argc, char *argv[]) 5{ 6 for (int i = 1; i < argc; i++) { 7 float f; 8 char *end; 9 f = strtof(argv[i], &end); 10 if (argv[i] != end) { 11 printf(u8" 通常表記: %f\n", f); 12 printf(u8"16進数表記: %a\n", f); 13 } else { 14 fprintf(stderr, u8"変換できません!\n"); 15 } 16 } 17 return 0; 18}
補足: FLT_RADIX(floatとかの基数)が2の乗数以外の場合
ほとんどの実装では浮動小数点数に2進数を用いており、このような2の乗数が基数の場合は16進数でも正確に表現できるため問題にはなりません。しかし、C言語では2進数でなければならないという制限はないため、16進数であっても正確に表現できるとは限りません。その場合については、別途規定されており、double型において区別するのに十分な(sufficient to distinguish values of type double)桁数とされています。詳しくは、C11仕様書(draft) N1570 §7.21.6.1 8 a,A p.313-314をご参考下さい。
補足2: strtofが正確なのかについて
2進数などの基数が2の乗数の場合、16進数表記であれば正確に解釈されます(N1570 §7.22.1.3 5 p.343参照)。しかし、これも基数が2の乗数で無い場合は、別途規定されており(N1570 §7.22.1.3 8 p.343参照)、正確に解釈できないため、その環境における丸め込みが行われます。上記の補足で述べた区別できる十分な桁数であれば丸め込みが起きても元々の情報から抜け落ちが発生するかどうかの規定は見受けられませんでしたが、「区別できる十分な桁数」という表現からすると、丸め込みが発生しても保持されるのに十分という解釈もできるかも知れません。2進数ではないC言語の実装系が手元に無い(というか、私は一度も見たこと無い)ので、詳しい検証はできていません。
補足3: NaNは保証できない
IEEE 754-2008では、NaNについてsignaling NaNとquiet NaNの2種類を区別するようになっていますが、C言語の仕様ではそのような区別が存在しないため、区別することができません。また、符号ビットや仮数部(ただし、全て0ではない)について任意の値が可能ですが、いずれの値もNaNと解釈され、C言語上では区別することができません。ビット列が異なるにもかかわらず、上記のprintfなどを用いた方法では"nan"としか表示できませんし、逆もまた同じで、strtofでNaNの時の各ビットを指定する方法がありません。つまり、この方法では精度を落とさずC言語上での同じ意味にすることはできますが、必ず同じビット列にできる方法ではありません。
投稿2015/12/11 22:26
編集2015/12/13 01:40総合スコア21739
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/12/12 22:01
2015/12/13 01:44
2015/12/13 03:30
2015/12/13 07:34 編集
2015/12/13 09:27
0
float 値を、めもりに書き込んで、ビットテストを32回繰り返せば2進数の文字列ができます。
これならば、元のfloat 変数に確実にもどせるはずですが。そういうことではなくて?
10111001 11100001 11011001 00010010
を8個ずつビットシフトで1バイト完成したらメモリに書き込み、4セット完了したら union で同じアドレスで定義した float 変数取ればいいかと。
c
1typedef union bitfloat_t 2{ 3 unsigned char bytes[4]; 4 float value; 5} bitfloat_t; 6unsigned char* float2bitstring(float value){ 7 bitfloat_t v; 8 v.value = value; 9 unsigned char* buffer = malloc(32 + 1); 10 buffer[32] = 0; 11 unsigned char bit = 0; 12 for (int x=0;x < 32;x++) { 13 bit = bit == 0 ? 0x80 : bit; 14 buffer[x % 8 + x / 8] = v.bytes[x/8] ^ bit ? '1' : '0'; 15 bit >>= 1; 16 } 17 return buffer; 18 19コード
C
1// TEST01.cpp : コンソール アプリケーションのエントリ ポイントを定義します。 2// 3 4#include "stdafx.h" 5 6#include <stdio.h> 7#include <stdlib.h> 8 9typedef unsigned char uchar; 10 11typedef union bitfloat_t { 12 float value; 13 uchar bytes[sizeof(float)]; 14} bitfloat_t; 15 16#define FLOATLEN sizeof(float) * 8 17 18float bits2float(uchar* buffer); 19void clearMem(uchar* buffer, int size); 20void float2bits(float v, uchar* buffer); 21void test(char *title, float val); 22 23int _tmain(int argc, _TCHAR* argv[]) 24{ 25 test("テスト1", 1e26F); 26 test("テスト2", 1.1234567890123456789F); 27 test("テスト3", 1.1234567e10F); 28 29 return 0; 30} 31 32void test(char *title, float val) { 33 34 bitfloat_t v; 35 uchar buffer[FLOATLEN +1]; 36 v.value = val; 37 38 float2bits(val, buffer); 39 40 printf("%s\n", title); 41 42 printf("実数値→変換:%f => %s\n", val, buffer); 43 44 float ff = bits2float(buffer); 45 46 printf("変換→実数値:%s => %f(%e)\n", buffer, ff, ff); 47 48 fgetc(stdin); 49 50} 51 52void clearMem(uchar* buffer, int size) { 53 for (int i=0;i < size;i++) buffer[i] = 0; 54} 55 56float bits2float(uchar* buffer) { 57 bitfloat_t vv; 58 clearMem(vv.bytes, sizeof(vv)); 59 60 int maxLen = FLOATLEN; 61 62 uchar bit = 0; 63 for (int i=0;i < maxLen;i++) { 64 if (bit == 0) bit = 0x80; 65 vv.bytes[i / 8] |= buffer[i] == '1' ? bit : 0; 66 bit >>= 1; 67 } 68 69 return vv.value; 70} 71 72void float2bits(float v, uchar* buffer) { 73 bitfloat_t vv; 74 vv.value = v; 75 76 int maxLen = FLOATLEN; 77 78 buffer[maxLen] = 0; 79 uchar bit = 0; 80 for (int i=0;i < maxLen;i++) { 81 if (bit == 0) bit = 0x80; 82 buffer[i] = vv.bytes[i / 8] & bit ? '1' : '0'; 83 bit >>= 1; 84 } 85} 86 87
投稿2015/12/11 14:04
編集2015/12/11 16:22総合スコア1693
0
「10進数文字列に変換」でなく「文字列に変換」であれば、浮動小数点のビットパターンをそのまま文字列として扱えば良いです。単精度なら4バイト、倍精度なら8バイトで可能。
印字可能文字にしたければ、16進数表現でも良いし、Base64エンコードでも良いし。この場合は、16進数ならバイト数は倍、Base64ならバイト数は4/3倍(6バイトか11バイト)。
投稿2015/12/11 14:36
総合スコア85901
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2015/12/12 01:36
2015/12/12 02:00 編集
2015/12/12 01:59
2015/12/13 07:23
2015/12/13 07:45
2015/12/13 07:54