- ベストアンサー
long double型の戻り値を持つ関数について
- StrToFloat()を用いた文字列の浮動小数点変換でケタ落ちが発生する
- 関数の戻り値がケタ落ちしてしまう理由について解説
- ポインタを使った関数の変更でもケタ落ちが解消されない
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
> そこで、デバッガを使って調べたところ、StrToValue(AnsiString str)のreturn直前では、 > result=-1.55519334183474013 > となっており、str を正確に数値に変換していました。 ということであれば、可能ならば、関数の宣言から _fastcall を外すと、 回避できるかもしれません。 > long double convert_2(char* str) > { > long double ret; > double a; > sscanf(str, "%lf", &a); > ret = a; > return ret; > } > a-kuma さんに作っていただいたこのプログラムでも、ret の値はreturn したときに後ろの数桁が消滅すると思われます。 こっちの関数は、*わざと* 精度を落としています。文字列→実数の展開を double で(つまり、%lf を使って)いるので、ret に代入している時点で long double の精度がありません。
その他の回答 (2)
- a-kuma
- ベストアンサー率50% (1122/2211)
C++Builder を持っていないので、他のコンパイラで試してみました。 試してみたコンパイラでは、long double を printf(),scanf() で扱うとき の書式指定子には %Lf を使います(C++Builder では、違うかもしれません)。 で、文字列を long double に変換する関数を二つ書いてみました。 #include <stdio.h> /* きちんと long double で扱う */ long double convert_1(char* str) { long double ret; sscanf(str, "%Lf", &ret); return ret; } /* double で扱う */ long double convert_2(char* str) { long double ret; double a; sscanf(str, "%lf", &a); ret = a; return ret; } int main() { char* str = "1111.1111111111111111111111111111111111111"; long double a; a = convert_1(str); printf("%30.20Lf\n", a); a = convert_2(str); printf("%30.20Lf\n", a); return 0; } 実行結果は、以下の通りです。 % prog 1111.11111111111111111111 1111.11111111111108584737 「桁落ちする」ってのは、こういうことですか?
補足
回答ありがとうございます。 説明不足だったようなので、少し補足しておきます。 StrToValue(AnsiString str)の実装部分は次の通りです。 long double __fastcall TGraphForm::StrToValue(AnsiString str) { long double count,result; int k; bool pm; k=str.Pos("."); if(k!=0) //小数点がある場合 { result=StrToFloat(str.SubString(1,k)); //resultに整数部分を代入 } else { //小数点がない場合 result=StrToFloat(str.SubString); return result; } if(StrToFloat(str)>=0) pm=true; //正の場合 else pm=false; str.Delete(1,k); //先頭から小数点までを削除 count=1.00000000000000000000000000000000000; count/=10; while(str!="") { if(str.SubString(1,1)!="E") //小数点以下の数値を1文字ずつresultに加えていく { if(pm) result+=StrToFloat(str.SubString(1,1))*count; else result-=StrToFloat(str.SubString(1,1))*count; } else { str.Delete(1,1); //E-10など の計算 result*=pow(10,StrToInt(str)); break; } str.Delete(1,1); //先頭の文字を削除 count/=10; } return result; } StrToValue(AnsiString str) を実際には次のように使っています。 void __fastcall TGraphForm::Input(AnsiString line) { long double RS; ・・・ RS=StrToValue(str); ・・・ } str="-1.55519334183474013" であるときに StrToValue(str) を実行したところ、 RS=-1.5551933418347 となりました。後ろの数桁が消滅してしまっています。 そこで、デバッガを使って調べたところ、StrToValue(AnsiString str)のreturn直前では、 result=-1.55519334183474013 となっており、str を正確に数値に変換していました。 しかし、return 後RSを調べたところ、やはり RS=-1.5551933418347 でした。 long double convert_2(char* str) { long double ret; double a; sscanf(str, "%lf", &a); ret = a; return ret; } a-kuma さんに作っていただいたこのプログラムでも、ret の値はreturn したときに後ろの数桁が消滅すると思われます。 (sscanf はこれまでに使ったことがなかったので、マニュアルで調べているところです) なにか心当たりの原因があるでしょうか?? お手数をかけてすみません。。
- a-kuma
- ベストアンサー率50% (1122/2211)
実際に、文字列を実数に変換しているところは、どうなっているのでしょう? long double StrToValue(AnsiString str) { long double value; value = str.ToDouble(); return value; } なんて、やってる? であれば、ToDouble() 自体が double を返すメソッドですから、精度が落ちる のは仕方ないですよね。 また、有効桁が落ちているのはどうやって確認したのでしょう? C++Builder の printf() や iostream は long double に対応しているので しょうか? # C++Builder を持ってないので、想像で書いてます 因みに、本題とは外れますが、 > long double StrToValue(AnsiString str) は、str の内容を変える関数ではないでしょうから、プロトタイプは long double StrToValue(const AnsiString& str) としたほうが良いです。
お礼
ありがとうございました。 関数の宣言から__fastcallを外して、TGraphFormのメソッドではない独立の関数にしたところ、うまく行きました。 いろいろお手数かけてすみませんでした。。