◀一般トップへ
  • 401 whileの中の判定
    • 406 Re:whileの中の判定
      • 408 Re2:whileの中の判定
        • 415 Re3:whileの中の判定
          • 416 Re4:whileの中の判定
        • 419 Re3:whileの中の判定
          • 421 Re4:whileの中の判定
            • 425 Re5:whileの中の判定
              • 426 Re6:whileの中の判定
                • 427 少しウソの部分訂正
                  • 428 Re: 少しウソの部分訂正
                    • 429 Re2: 少しウソの部分訂正
                      • 430 Re3: 少しウソの部分訂正
  • [401] whileの中の判定 じぇぷろ 2001年05月16日 18:47

    全然関係ない話で恐縮ですが、C言語のことでわかる方教えて頂けないでしょうか?某所でQしたのですがAが付かなくて。。。(T_T)
    それは単精度と倍精度の話です。
    単精度でのマシンエプシロンを計算するために
    float a=1.0f;
    while(1.0f+a>1.0f){
      a*=0.5f;
      printf("a=%24.20e\n",a);
    }
    とやっても倍精度の値が出てしまいます。VC++6.0のコマンドライン版MS-C12.0使用。LSI-Cでも同様でした。UNIXのCCコマンドも。ところがMacのCやLinuxのgccではちゃんと単精度で出ました。またjavaでも。コンパイラによるということはわかったのですが、限りなくバグに近い気もします。while((b=1.0f+a)>1.0f) (bもfloat宣言)でもダメでしたが、(b>1.0f)とするとOKでした。
    Cの言語仕様としてこれはどうなんでしょう? 単精度も一度倍精度化してから演算するコンパイラがあるらしいのでそれによって'+' しているのかなと思いますが、while内の'>'の判定もそうなってしまうんでしょうか?こういう理解でいいのかな?すっきりしないけど。プログラマの意図を汲んでくれないなんて。。。
    • [406] Re:whileの中の判定 すとーにぃ 2001年05月16日 23:32

      ▼ じぇぷろさん
      > それは単精度と倍精度の話です。
      略_(._.)_
      > Cの言語仕様としてこれはどうなんでしょう? 単精度も一度倍精度化してから演算するコンパイラがあるらしいので

      このへんの動作を書いた記事が下記のURLにありました。
      http://www.pro.or.jp/~fuji/mybooks/cdiag
      /cdiag.4.4.html


      目次はここです。
      http://www.pro.or.jp/~fuji/mybooks/cdiag
      /index.html

      • [408] Re2:whileの中の判定 じぇぷろ 2001年05月17日 00:54

        ▼ すとーにぃさん
        > このへんの動作を書いた記事が下記のURLにありました。
        > http://www.pro.or.jp/~fuji/mybooks/cdiag
        /cdiag.4.4.html

        どうも有難うございます。なーるほど、といいたいところですが、私も一応某所でQする前にWebであれこれ調べておりまして、それでもわからなかったので質問に及んだのです。
        自助努力するには便利な時代ですからね。それでわかれば話が早いし。で、このページも見ました。というか本自体持ってます。(いい本です)
        じぇぷろ>単精度も一度倍精度化してから演算するコンパイラがあるらしいので
        がまさにそれで知った情報です。ただ別の本では早くなるようにfloat同士で計算するように設計されているものもある(ものが多いだったか?)と書いてました。
        しかし私の問題にしているのは計算速度ではないんですよね。比較演算子もダブルでやってくれちゃうの?私はご丁寧に定数にもf付けてるのに。(ーー;)
        という点なんです。さらにいうとwhileの前と中でb=1.0f+a;を二度書きしなくてもいいCの利点を使ってせっかくwhile((b=1.0f+a)>1.0f)にしてるのにこれじゃだめで
        while(b>1.0f)でしか思う通りに動作しない(コンパイラもある)という点なんです。演算自体はdoubleでやってもいいけど、比較演算子はfloatでやれよ、みたいな。それくらいCの仕様で固定しとけよ、みたいな。
        • [415] Re3:whileの中の判定 すとーにぃ 2001年05月18日 21:24

          ▼ じぇぷろさん
          > で、このページも見ました。というか本自体持ってます。(いい本です)
          ....φ(..;;

          > while((b=1.0f+a)>1.0f)にしてるのにこれじゃだめで
          左辺がdouble, 右辺がsingleに展開される->倍精度で比較
          とか。。。

          #ヘルプを2ヶ月ぶりに更新しました。
          • [416] Re4:whileの中の判定 じぇぷろ 2001年05月18日 22:05

            ▼ すとーにぃさん
            > ▼ じぇぷろ
            > > while((b=1.0f+a)>1.0f)にしてるのにこれじゃだめで
            > 左辺がdouble, 右辺がsingleに展開される->倍精度で比較

            bもfloatにしてあってもダメなんす。1+aをダブルで計算し、bに単精度で入れる。ケド比較はダブルでやってる
            #そういう意味じゃなかった?
            #あ、もしかして式の値が右辺を使うから?そういう意味?比較はbとではなく1+aでやってるから?
            #while( (b=(float)(a+1.0f)) > 1.0f ) にしてもやっぱりだめだった。結局できなんだな。

            因みに昨日WinソフトであるCodewarriorのCでやってみましたがこれでもだめでした。gccとMacのシマンテックCだけエライ。

            > #ヘルプを2ヶ月ぶりに更新しました。
            お疲れ様。これからDLして鑑賞します。(^^ゞ
        • [419] Re3:whileの中の判定 げんた 2001年05月19日 20:10

          これは、仮数部の精度を調べているんですよね.

          ▼ じぇぷろさん
          > while((b=1.0f+a)>1.0f)にしてるのにこれじゃだめで
          > while(b>1.0f)でしか思う通りに動作しない
          VC6でアセンブラ出力を吐き出させて、どうなっているか確認してみました.

          結論から言えば、1.0f+a>1.0fはFPUで1.0f+aの計算を行って引き続き1.0fと比較しています.ということはFPUのレジスタ精度(80bit浮動小数点)で演算が行われているはずです.となると、FPUでは80bitの形式で演算&比較を行っているのにどうして倍精度の値しか出ないのかが逆に不思議.10^(-20)くらいの値が出てきてもおかしくないのに.
          IA-32 Intel Architecture Software Manual↓
          http://developer.intel.com/design/Pentiu
          m4/manuals/24547003.pdf


          あと、最適化オプションによっては変数bを介しても倍精度の結果が出ました.これは規格から外れた動作らしく、それを防ぐオプション(/Op)が用意されています.
          • [421] Re4:whileの中の判定 じぇぷろ 2001年05月19日 21:01

            ▼ げんたさん
            > これは、仮数部の精度を調べているんですよね.
            そうです。

            > ▼ じぇぷろ
            > > while((b=1.0f+a)>1.0f)にしてるのにこれじゃだめで
            > > while(b>1.0f)でしか思う通りに動作しない
            > VC6でアセンブラ出力を吐き出させて、どうなっているか確認

            > 結論から言えば、1.0f+a>1.0fはFPUで1.0f+aの計算を行って引き続き1.0fと比較しています.ということはFPUのレジスタ精度(80bit浮動小数点)で演算が行われているはずです.となると、FPUでは80bitの形式で演算&比較を行っているのにどうして倍精度の値しか出ないのかが逆に不思議.
            どうもお手間を取らせました。有難うございます。m(__)m 後半はわかりませんが、一応推測通りだったわけですね。あー一応すっきりした。処理のし方自体は納得いかない(不満という意味)けど。どう頑張っても単精度変数に一度落としてからでないと比較できないなんてアルゴリズム上の問題じゃないよなあ。

            > あと、最適化オプションによっては変数bを介しても倍精度の結果が出ました.これは規格から外れた動作らしく、それを防ぐオプション(/Op)が用意されています.
            困ったモンです。>MS それに引き換えgccはフリーなのにさすがだな。変数も定数も単精度指定してるんだから比較も単精度でやってくれるものと思うでしょ、普通。
            • [425] Re5:whileの中の判定 げんた 2001年05月21日 09:53

              > どう頑張っても単精度変数に一度落としてからでないと比較できないなんてアルゴリズム上の問題じゃないよなあ。
              ISO C++ Standard
              http://www.cygnus.com/misc/wp/dec96pub/c
              oversheet.html
              のChapter 5
              http://www.cygnus.com/misc/wp/dec96pub/e
              xpr.html
              項目10によれば、

              10. The values of the floating operands and the results of floating expressions may be represented in greater precision and range than that required by the type; the types are not changed thereby.2)
              となっているので、そういうものだと思うしか無いと思います。今回のように無理矢理精度を落としたいという方がまれだと思いますが。

              C++には表現できる値の範囲を調べるためのlimitsというライブラリ(Chapter 18)がありますけど、それじゃだめなの?

              floatをdoubleに変換するという話はAnnex C (Compatibility)の1.1.1の項目3に
              3 Single-precision floating point arithmetic may be used for float expressions; _basic.fundamental_ and _conv.double_. This is also in ISO C.
              とありますので、昔はdoubleに変換するのがスタンダードな動作だったが1985年の規格制定時に改められたと解釈できそうです。でもこれは、今回の話とはたぶん関係ない。

              > 困ったモンです。>MS それに引き換えgccはフリーなのにさすがだな。
              うーん、どうだか。1.0+a==1.0というのは明らかに間違いですからaの精度以上に正しい結果(1.0+a>1.0)を出す方が親切と考える人がいてもおかしくない。それに、いちいち精度を変換して比較するのはそれだけ遅くなっているとも考えられますし。
              • [426] Re6:whileの中の判定 じぇぷろ 2001年05月21日 17:54

                ▼ げんたさん
                > > どう頑張っても単精度変数に一度落としてからでないと比較できないなんてアルゴリズム上の問題じゃないよなあ。
                > そういうものだと思うしか無いと思います。今回のように無理矢理精度を落としたいという方がまれだと思いますが。
                (途中省略)
                > > 困ったモンです。>MS それに引き換えgccはフリーなのにさすがだな。
                > うーん、どうだか。1.0+a==1.0というのは明らかに間違いですからaの精度以上に正しい結果(1.0+a>1.0)を出す方が親切と考える人がいてもおかしくない。それに、いちいち精度を変換して比較するのはそれだけ遅くなっているとも考えられますし。

                そういうことではないんです。主旨は「計算には精度というものがあり、その範囲でしか計算結果を信用してはいけない」という教育的内容なわけです。だから内部的には1.0+a=1.0ということも起こり得るという意味で誤りではありません。精度よく計算したい場合には、あるいは予期しない結果になっていた場合には精度の影響が考えられるので高精度で計算しなさいという主旨。
                Fortranでは倍精度の他に四倍精度もほぼ標準的にあります。(JIS規格かまでは知りませんが)
                一方Cは長精度は処理系によってはありますが、長さまで規定されてないし、1.5倍とかいい加減です。
                言語の標準精度はだいたい単精度で、Cのみ倍精度とちょっと変わっている、と思っているのですが、最近のPascal(Delphi)やBasic(VB)、javaなどはどうなんでしょ?
                別に倍精度と長精度の比較でもいいんですが、標準的ではないのと処理系によって異なるというのを嫌ったわけです。結果的にはそれも(プログラムの書き方により)処理系によってしまったわけですが。長精度でやっても今度は長精度で比較してほしいところを倍精度で比較されてしまうわけだから(ですよね?)やっぱり良くない仕様だと思います。
                単精度は電卓にも劣る精度だとかわざわざ精度を落として計算するのはあほらしい、とか今では単精度の方がむしろ時間がかかる、というのは精度ということを知ることとは別の次元の、むしろもっと泥臭い話です。コンパイラの作りによるわけだから。
                精度という考えはどの計算用言語にもある話です(よね?)。結局、C言語では単精度を使うといろいろ予期しないが起こり得るし、使うメリットもほとんどないので倍精度で計算しなさい、というあまり明解でない回答になってしまいます。
                • [427] 少しウソの部分訂正 じぇぷろ 2001年05月22日 10:54

                  ▼ じぇぷろさん
                  > Fortranでは四倍精度
                  JIS規格には規定されてないようです
                  > Cは長精度は処理系によってはありますが、長さまで規定されてないし、1.5倍とかいい加減です。
                  長精度は正確には拡張倍精度というみたい。長さは単4バイト、倍8バイト、拡張倍10バイトだから1.25倍が正しい
                  げんた氏の疑問の有効桁数約20桁(16x1.25)はこれから出てくるのでは? 結局実はCは(拡張)倍精度しかもってないのか?
                  短くして単精度、目一杯とって拡張倍精度といってるだけ?ようわからん。
                  • [428] Re: 少しウソの部分訂正 げんた 2001年05月22日 16:21

                    >長精度は正確には拡張倍精度というみたい。長さは単4バイト、倍8バイト、拡張倍10バイト
                    規格上はdoubleはfloatと同等以上の精度と数値範囲、long doubleはdoubleと同等以上の精度と数値範囲としか記載されていないのでコンパイラやOSを指定しないでこういう表現を使うのは誤解を招くと思います。(3.9.1 Fundamental typesの第8項参照)

                    極端な話 float == double == long doubleという処理系もOKなわけで、処理系依存だからこそsizeof演算子とかlimitsライブラリとかがあるわけです。

                    規格をよく見たら4.6 Floating point promotionという項がありまして、
                    http://www.cygnus.com/misc/wp/dec96pub/c
                    onv.html#conv.fpprom
                    1 An rvalue of type float can be converted to an rvalue of type double. The value is unchanged.
                    だそうです。ですから前回のと併せて考えてdoubleに変換するもしないも処理系依存と言えます。
                    • [429] Re2: 少しウソの部分訂正 じぇぷろ 2001年05月22日 20:48

                      ▼ げんたさん
                      > >長精度は正確には拡張倍精度というみたい。長さは単4バイト、倍8バイト、拡張倍10バイト
                      >コンパイラやOSを指定しないでこういう表現を使うのは誤解を招く
                      > 極端な話 float == double == long doubleという処理系もOK
                      可能性としてはあるのでしょうが、そんなのはないと思うし、あっても誰も使わないでしょう。ごく一般的な、普通の(したがって良く使われている)コンパイラを対象にいっているのです。JIS規格のCといいかえてもいいですがどこまで規定しているのかははっきりしません。ただDOS系では拡張倍精度は10バイトが多いらしいことは本に書いてあります。

                      > doubleに変換するもしないも処理系依存と言えます。
                      処理系依存部分が多いというのは規格の主旨としてはどうかな?がちがちなのも困るけど緩すぎるのも規格の意味が薄れる。ともかくCはそんなもんだと思うしかない。
                      • [430] Re3: 少しウソの部分訂正 げんた 2001年05月22日 21:23

                        >可能性としてはあるのでしょうが、そんなのはないと思うし、
                        double == long double という処理系は見たことがあるような気がします。。

                        >> doubleに変換するもしないも処理系依存と言えます。
                        > ともかくCはそんなもんだと思うしかない。
                        16bit CPUではsizeof(int) == sizeof(short int) == 2だったけど、32bitになったらsizeof(int) == sizeof(long int) == 4 とかね。既に広まってしまったものを規格化したのでこうなったのでしょうか。個人的にはCは雰囲気が比較的アセンブラに近いというイメージを持っています。コンパイル後のイメージが想像できる。