5

Cのさまざまな浮動小数点形式(つまり、float、double、long double)の精度レベルを調べようとしています。現在使用しているコードは次のとおりです。

#include <stdio.h>
#define N 100000

int main(void)
{
   float max = 1.0, min = 0.0, test;
   int i;                              /* Counter for the conditional loop */

   for (i = 0; i < N; i++) {
      test = (max + min) / 2.0;
      if( (1.0 + test) != 1.0)         /* If too high, set max to test and try again */
     max = test;
  if( (1.0 + test) == 1.0)     /* If too low, set min to test and try again */
         min = test;
   }
   printf("The epsilon machine is %.50lf\n", max);
   return 0;
}

これにより、予想どおり約2^-64の値が得られます。ただし、減速度を2倍または「長い2倍」に変更すると、同じ答えが得られますが、小さい値を取得する必要がありますが、そうではありません。誰かアイデアがありますか?

4

6 に答える 6

9

それはあなたが「精度レベル」によって何を意味するかによります。

浮動小数点数には「通常の」(通常の)値がありますが、特別な非正規化数もあります。さまざまな制限を知りたい場合は、C標準に事前定義された定数があります。

#include <math.h>
#include <stdio.h>
#include <float.h>

int main(void)
{
    printf("%30s: %g\n", "FLT_EPSILON", FLT_EPSILON);
    printf("%30s: %g\n", "FLT_MIN", FLT_MIN);
    printf("%30s: %g\n", "nextafterf(0.0, 1.0)", nextafterf(0.0, 1.0));
    printf("%30s: %g\n", "nextafterf(1.0, 2.0)-1", (nextafterf(1.0, 2.0) - 1.0f));
    puts("");
    printf("%30s: %g\n", "DBL_EPSILON", DBL_EPSILON);
    printf("%30s: %g\n", "DBL_MIN", DBL_MIN);
    printf("%30s: %g\n", "nextafter(0.0, 1.0)", nextafter(0.0, 1.0));
    printf("%30s: %g\n", "nextafter(1.0, 2.0)-1", (nextafter(1.0, 2.0) - 1.0));
    puts("");
    printf("%30s: %Lg\n", "LDBL_EPSILON", LDBL_EPSILON);
    printf("%30s: %Lg\n", "LDBL_MIN", LDBL_MIN);
    printf("%30s: %Lg\n", "nextafterl(0.0, 1.0)", nextafterl(0.0, 1.0));
    printf("%30s: %Lg\n", "nextafterl(1.0, 2.0)-1", (nextafterl(1.0, 2.0) - 1.0));
    return 0;
}

上記のプログラムは、タイプごとに4つの値を出力します。

  • 1とそのタイプ( TYPE_EPSILON)の1より大きい最小値の差、
  • 特定のタイプ( TYPE )の最小の正の正規化値_MINこれには非正規化数は含まれません、
  • 特定のタイプの最小の正の値(nextafter* (0... ))。これには非正規化数が含まれます
  • 1より大きい最小数。これはTYPE_EPSILONと同じですが、計算方法が異なります。

「精度」の意味に応じて、上記のいずれかまたはまったく役に立たない場合があります。

これが私のコンピューターでの上記のプログラムの出力です:

               FLT_EPSILON: 1.19209e-07
                   FLT_MIN: 1.17549e-38
      nextafterf(0.0, 1.0): 1.4013e-45
    nextafterf(1.0, 2.0)-1: 1.19209e-07

               DBL_EPSILON: 2.22045e-16
                   DBL_MIN: 2.22507e-308
       nextafter(0.0, 1.0): 4.94066e-324
     nextafter(1.0, 2.0)-1: 2.22045e-16

              LDBL_EPSILON: 1.0842e-19
                  LDBL_MIN: 3.3621e-4932
      nextafterl(0.0, 1.0): 3.6452e-4951
    nextafterl(1.0, 2.0)-1: 1.0842e-19
于 2010-08-13T19:10:30.720 に答える
2

I'm not sure how your algorithm is supposed to work. This one (C++) gives the correct answers:

#include <iostream>

template<typename T>
int epsilon() {
    int pow = 0;
    T eps = 1;
    while (eps + 1 != 1) {
        eps /= 2;
        --pow;
    }
    return pow + 1;
}

int main() {
    std::cout << "Epsilon for float: 2^" << epsilon<float>() << '\n';
    std::cout << "Epsilon for double: 2^" << epsilon<double>() << '\n';
}

This computes the smallest value such that, when added to 1, is still distinguishable from 1.

Output:

Epsilon for float: 2^-23
Epsilon for double: 2^-52
于 2010-08-13T16:32:58.063 に答える
2

同じ答えが得られる理由を推測してください。

if( (1.0 + test) != 1.0)

ここで、1.0はdouble定数であるため、floatをdoubleに昇格させ、加算をdoubleとして実行します。ここで一時的なfloatを宣言して加算を実行するか、これらのfloat数値定数(1.0fIIRC)を作成することをお勧めします。

また、一時的な浮動小数点数の超過精度の問題に陥っている可能性があり、正しい精度に下げるために中間体をメモリに格納するように強制する必要がある場合があります。


範囲検索方法をやり直して、正しいタイプでテストを計算する方法を簡単に説明します。でも、少し大きすぎる答えが返ってきます。

#include <stdio.h>
#define N 100000
#define TYPE float

int main(void)
{
   TYPE max = 1.0, min = 0.0, test;
   int i;

   for (i = 0; i < N; i++)
   {
      TYPE one_plus_test;

      test = (max + min) / ((TYPE)2.0);
      one_plus_test = ((TYPE)1.0) + test;
      if (one_plus_test == ((TYPE)1.0))
      {
         min = test;
      }
      else
      {
         max = test;
      }
   }
   printf("The epsilon machine is %.50lf\n", max);
   return 0;
}
于 2010-08-13T16:22:06.573 に答える
2

IEEE 754浮動小数点形式には、同じ幅の2の補数整数として再解釈されると、正の値に対して単調に増加し、負の値に対して単調に減少するという特性があります(32ビット浮動小数点数のバイナリ表現を参照)。また、0 <| f(x)|というプロパティもあります。<∞、および| f(x + 1)− f(x)| ≥|f(x)− f(x−1)| (ここで、f(x)は前述のxの整数再解釈です)。型のパンニングを許可し、常にIEEE 754-1985を使用する言語では、これを利用して一定時間で計算機イプシロンを計算できます。たとえば、Cでは:

typedef union {
  long long i64;
  double d64;
} dbl_64;

double machine_eps (double value)
{
    dbl_64 s;
    s.d64 = value;
    s.i64++;
    return s.d64 - value;
}

https://en.wikipedia.org/wiki/Machine_epsilonから

于 2016-05-22T20:05:58.030 に答える
1

を使用して浮動小数点計算から最高の精度を得ることができることを付け加えたいと思いますlong double

これを@Rupのソリューションに適用するには、TYPEtolong doubleprintfステートメントを次のように変更します。

printf("The epsilon machine is %.50Lf\n", max);

これは私のマシンで使用しているイプシロンfloatです:

0.00000005960465188081798260100185871124267578125000

そして使用long double

0.00000000000000000005421010862427522170625011179761

その違いは非常に重要です。

于 2016-01-25T00:31:53.667 に答える
0

このようなコードの問題は、コンパイラが浮動小数点変数をマイクロプロセッサの浮動小数点レジスタにロードすることです。また、マイクロプロセッサに倍精度浮動小数点レジスタしかない場合、精度はとで同じにfloatなりdoubleます。

2つの計算の間に、浮動小数点値をメモリに(正しいタイプの変数に)格納するようにコンパイラーに強制する方法を見つける必要があります。そうすれば、レジスタの追加の精度を捨てる必要があります。しかし、今日のコンパイラーはコードを最適化するのに賢いです。したがって、これを達成するのは難しい場合があります。

于 2010-08-13T18:38:59.167 に答える