C言語関係掲示板

過去ログ

No.39. 浮動小数点数での四捨五入


たびたび同一人物で恐縮です。
前回の課題は完成したのですが新たな課題を進める上でまたつまずいてしまい、
結局、個人では解決できずに質問させていただいています。
(誰か書き込まないかと待っていたのですが、つい待ちきれなくて・・・。すいません)

四捨五入関数をmodfを利用してつくったのですが
戻り値が異常な値になってしまいます。(1万近いアドレスのような値です)
引数は正常な値がはいっており、型も間違っていません。
本を調べても引数や型は間違っていないように感じます。
ご指摘をよろしくお願いします。

int round( double num ){
double intp=0.0;
double dec;

dec = modf( num, &intp );

if( dec >= 0.5 ) num = intp + 1.0;
else if( dec < 0.5 ) num = intp;

return num;
}


>四捨五入関数をmodfを利用してつくったのですが
>戻り値が異常な値になってしまいます。(1万近いアドレスのような値です)
>引数は正常な値がはいっており、型も間違っていません。
>本を調べても引数や型は間違っていないように感じます。

はじめまして、Toshi_2と申します。
しん様のお作りなった関数をコピーして
main()部を書き加えたところ、私の環境では正常に機能していました。

以下のように書いてみましたのでご参考にして下さい。
ですので、もしかしたら呼び出しに問題があるのでは?と思います。

/* Borland C++ Compiler 5.5 と cl.exe(Visual C++添付分)使用 */
int round(double num)
{
double intp=0.0;
double dec;

dec = modf(num, &intp);

if (dec >= 0.5) num = intp + 1.0;
else if (dec < 0.5) num = intp;

return (int)num;
}

int main(void)
{
double a = 135.5, b = 2358.333;

printf("%d\n", round(a));
printf("%d\n", round(b));

return (0);
}

PS.私はC言語を始めてからまだ日が浅いのでもし間違ってるところ
   があればご指摘お願いします。


さっそくご指摘ありがとうございました。

さっそく見せていただいたプログラムを走らせたところうまくいきましたので、やはりどこかに間違いがあると思います。(関数なので絶対に使い方に問題があると思います。)
しかし、たしかに先のプログラムでは正常に動くのですが、やはり、いまつくっているプログラムでは正常に動作しません。
プログラム自体は長いのですが、この関数に関しては入力と出力は単純なので、どこで間違えているのかさっぱりです。
前回より詳細を記載させていただきますので、ご指摘いただければ幸いです。int round( double num ){

<PRE>
struct Line{
double xstart,xend;
int int_xstart, int_xend;
}

void Scantriangle( 省略 ){
struct Line triline;
double xcomp[2];

計算省略

triline = xcomp[0];(365.000000がはいっています)
printf("triline.xstart = %f\n", triline.xstart);

triline.int_xsart = round( triline.xstart );

printf("triline.int_xstart = %d\n", triline.int_xstart);

}

round( double num ){
double intp=0.0;
double dec;

dec = modf(num, &intp);
printf("dec = %f\n", dec);
if (dec >= 0.5) num = intp + 1.0;
else if (dec < 0.5) num = intp;

return (int)num;
}
</PRE>

ちなみにScantriangleはmain関数の中で呼ばれています。
また、上記のプログラム中のprintfの中身を見てみると
round関数のすぐ上のprintfでは365.000000が入っており、
round関数内のprintfでは1081528320.000000が入っており、
round関数のすぐ後のprintfでは366が表示されています。

どうぞよろしくお願いします。


しんさん、こんにちは。ソース拝見しました。
triline = xcomp[0]; でコンパイルエラーになりますが、これは
triline.xstart = xcomp[0]; の間違いですか?
下のようにしてみましたが、特におかしなところはありませんでした。
ためしに、round関数内で modf を呼ぶ前に printf でnumの値を見てくれませんか。

それから、四捨五入ですが、modf を使わなくても出来るので、次の方法を
参考にしてみてください。

/* 小数点以下四捨五入 */
#include <stdio.h>
int main(void)
{
        double x = 123.499, y = 123.5;
        int     a,b;

        a = x + 0.5;
        b = y + 0.5;

        printf("a: %d  b: %d\n",a,b);

        return(0);
}


------- 試したプログラム -----------

#include <stdio.h>
#include <math.h>
struct Line{
        double xstart,xend;
        int int_xstart, int_xend;
};

void Scantriangle(void){
        struct Line triline;
        double xcomp[2];

/* 計算省略 */
        xcomp[0] = 365.0;
/*      triline = xcomp[0];     /*(365.000000がはいっています)*/
        triline.xstart = xcomp[0];
        printf("triline.xstart = %f\n", triline.xstart);

        triline.int_xstart = round( triline.xstart );

        printf("triline.int_xstart = %d\n", triline.int_xstart);

}

round( double num ){ 
        double intp=0.0;
        double dec;

        dec = modf(num, &intp);
        printf("dec = %f\n", dec);
        if (dec >= 0.5) num = intp + 1.0;
        else if (dec< 0.5) num = intp;

        return (int)num;
}

void main(void)
{
        Scantriangle();
}


#比較の<>があると、<PRE>はうまく動作しないんですね。すみません。


レスを頂いてから私も、printf()をあちこちに埋め込んでから
結構色々書いてコンパイルしてみたのですが
きちんと動いているのでどこが悪いのかが特定できません。
力足らずで申し訳ありません。


>triline.xstart = xcomp[0]; の間違いですか?

これは私も疑問でしたが、なんせ私C言語はおろかプログラミングを
始めて一ヶ月程の者でして、
もしかしてこういう書き方があるのかなぁ?などと思っていました。
(まだ入門書の半分くらいしか進んでないので構造体は未勉強範囲
 なのでした。)


あとは、ほか部分での配列オーバーとかポインタ関連の見えないバグとか
倍精浮動小数点の内部表現の限界によるずれ、とかぐらいしか私のレベル
では頭に浮かんできません。

>それから、四捨五入ですが、modf を使わなくても出来るので、次の方法を
>参考にしてみてください。

>/* 小数点以下四捨五入 */
>#include <stdio.h>
>
int main(void)
{
double x = 123.499, y = 123.5;
int a,b;

a = x + 0.5;
b = y + 0.5;

printf("a: %d b: %d\n",a,b);

return(0);
}



しん様のmodfを使った関数にも「すごいなぁ」と感心させられましたが
ともじ様のこの考え方はすごいですね!!
これを読んだ瞬間、なんかこう、、、足下をすくわれたような、、
一本とられたような衝撃にみまわれました。

しかし、しん様もすごい勉強をなさったのでしょうね。
前回の投稿を見ていたら、全くちんぷんかんぷんで
私が勉強して、果たしてしん様のようになれるのだろうか?
と不安になってきました。

でも、この掲示板に書き込みをしただけで、modf()関数の使い方を
マスターできたのでしん様には感謝感謝です。(初めて目にしたもので)
ありがとうございました。


Toshi_2さんこんにちは、先日は書き込み有難うございました。
_2さんということは、他にもToshiさんがいるんですね。私の知り合いにも
バリバリのToshiさんがいますよ。

プログラムを初めて1ヶ月でよくここまで勉強しているものだと、感心しています。
1年もすると、私の知っているToshiさん並のバリバリになるかもしれませんね。

私が提示した四捨五入のやり方は、アルゴリズムの入門書に出ていたやり方です。
私が考え出したものではないんですね。(^_^;)
こういった仕事をしていると、基礎知識みたいなものに助けられることも多いものです。
色々と勉強すると言う意味で、これからも書き込みお願いします。


ともじ様こんにちは、そしてはじめましてToshi_2と申します。

>プログラムを初めて1ヶ月でよくここまで勉強しているものだと、
>感心しています。

ありがた〜いお言葉ありがとうございます。
でもちょっと誉め過ぎの表現ですよ。
しかし、運悪く私の周りにはプログラミングはおろかパソコンを使う人
もいないので一般の書籍や、ともじ様のホームページなどだけが頼りです。
ですので、変な癖やすごい回り道をしているのではないだろうか?
などとよく不安になります。

>色々と勉強すると言う意味で、これからも書き込みお願いします。

もしご迷惑でなければこれからもわかる範囲だけでも書き込みを
させてください。
自分の理解していることを確認する場所が私にはないので、
このような掲示板等を利用させていただきたいのです。
どうぞ、これからもよろしくお願いいたします。



ともじさん、Toshi_2さん、いつもお世話になってます。
しんです。

ともじさん、printfの中身についてお伝えするのがおそくなってすいません。
実はレスを書いたつもりだったのですが、よく見たらアップされてませんでした。
(No.9くらいだと思ったのですが、自分で消してしまったのかなぁ?)

ご指摘のとおりtriline = xcomp[0]は、triline.xstart = xcomp[0]です。
加えて、round関数の直前のnumの値ですが、愚直に行った実験結果を書かせてもらいます。

1.round関数の前にprintf("num = %f\n", num)を置く
ー>コンパイラにnumが未宣言と怒られる。
2.Scantriangle関数内にdouble型のnumを宣言、上記のprintfで確認する
ー>14928437493739382921032849383272948382984778789200018177392901のような数字が入っていました。

原因がやはり、よくわかりません。double型が処理系によりおかしくなっているのでしょうか?さっぱりです。

余談ですが、私のCの能力はまだ全然なので勉強中なのですが
奇遇にも私がCを頑張って勉強しようと思ったきっかけは、友人の「とし」という人間の影響でした。
彼はとてもプログラムができる人間でしたが、
Toshiという名の人はプログラムに強いのかもしれませんね?


しんさん、今晩は。

> 実はレスを書いたつもりだったのですが、よく見たらアップされてませんでした。

ここの掲示板システム、確認画面が出るので、それで送った気になったのではないでしょうか。

> 2.Scantriangle関数内にdouble型のnumを宣言、上記のprintfで確認する
ー>14928437493739382921032849383272948382984778789200018177392901のような数字が入っていました。

すごい数字ですね。ひょっとしたら、http://www.catnet.ne.jp/kouno/c_faq/c14.html
の14.13: あたり参考になりませんか。

それから、
triline.xstart = xcomp[0];
triline.int_xstart = round( triline.xstart );

triline.xstart = xcomp[0];
triline.int_xstart = round( xcomp[0] );
でも、結果は同じでしょうか。


ともじさん、こんにちわ。

>triline.xstart = xcomp[0];
>triline.int_xstart = round( xcomp[0] );
>でも、結果は同じでしょうか。
>http://www.catnet.ne.jp/kouno/c_faq/c14.html
>の14.13: あたり参考になりませんか。

さっそくやってみたのですが、残念ながら結果は同じでした。
どうやら、教えていただいたHPに書いてある通り、10進数や2進数の変換の際の
問題かもしれませんね。

こういったこと(変換の際の問題など)が起きるなんて、全然知らなかったので
今回はほんとに勉強になったと思います。
自分のつくった四捨五入はうまくいきませんでしたが、ともじさんやToshi_2さんの
アドバイスはとても参考になりました。ありがとうございました。



>>http://www.catnet.ne.jp/kouno/c_faq/c14.html
>>の14.13: あたり参考になりませんか。
>
>どうやら、教えていただいたHPに書いてある通り、
>10進数や2進数の変換の際の
>問題かもしれませんね。
>
>こういったこと(変換の際の問題など)が起きるなんて、全然知らなかったので
>今回はほんとに勉強になったと思います。

これについて、参考になるスクリプトを見つけてきました。

#include <stdio.h>

int main(void)
{
float x;

for (x = 0.0; x <= 1.0; x += 0.01)
printf("x = %f\n",x);

return (0);
}
*柴田望洋、「定本・明解C言語 入門編」、ソフトバンク、1999
174ページより引用

上記のプログラムを実行するとx(単精度浮動小数点型)の数値が最終的に0.999999になります。
(もちろん処理系・環境によっては他の数値になり得ますが)

私の作ったプログラムの中にも(double)倍精度浮動小数点型の数値が論理値と
ずれて表現されるものがいくつかありますが、分かり易い例ではないので
引用をさせていただきました。

私は"Windows98 + Vc++6.0付属cl.exe and Lsic86試食版 and
Borland C++ Compiler5.0" と "Windows2000 + Lsic86試食版 and
Borland C++ Compiler5.0"の二つの環境と3つのコンパイラで
ためしてみましたが最終値は全て"0.999999"を表示しました。
ちなみにこの3つの処理系はfloat型はいずれもIEC559の
単精度浮動少数点数フォーマットを内部表現として使用している
そうですのでほとんど同じ数値を指すのは当然かもしれませんね。


一度お試しになって確認したら浮動小数点の問題が実感できると思います。
浮動小数点はあなどれませんね。

戻る


「初心者のためのポイント学習C言語」 Last modified:2001.8.8
Copyright(c) 2000-2002 TOMOJI All Rights Reserved