1

以下のコードでmpf_addは、2 つの浮動小数点値の文字列表現を追加するために使用しています。この時点で私が理解していないのは、その理由2.2 + 3.2 = 5.39999999999999999999999999999999999999です。gmpを与えるのに十分賢いと思っていたでしょう5.4

gmp がどのようにフロートするかについて、私は何を理解していませんか?

(ところで、これを最初に書いたとき、小数点を挿入する方法がわからなかったので、最後にプラス/マイナスの数字が入ります)

BSTR __stdcall FBIGSUM(BSTR p1, BSTR p2 ) {
  USES_CONVERSION;

  F(n1);
  F(n2);
  F(res);

  LPSTR sNum1 = W2A( p1 );
  LPSTR sNum2 = W2A( p2 );

  mpf_set_str( n1, sNum1, 10 );
  mpf_set_str( n2, sNum2, 10 );

  mpf_add( res, n1, n2 );

  char * buff =  (char *) _alloca( 1024 );
  char expBuffer[ 20 ];
  mp_exp_t exp;

  mpf_get_str(buff, &exp, 10, 0, res);

  char * temp = ltoa( (long) exp, expBuffer, 10 );
  if (exp >= 0) {
    strcat(buff, "+" );
  }
  strcat(buff, expBuffer );

  BSTR bResult = _com_util::ConvertStringToBSTR( buff );
  return bResult;
}
4

3 に答える 3

4

これは、バイナリ環境で浮動小数点演算を使用する固有のエラーが原因です。

詳細については、IEEE 754標準を参照してください。

于 2008-10-07T15:18:00.127 に答える
1

ウォーレン が言ったこと。

浮動小数点数の代わりに 2 進化 10 進数を使用すると、より良い結果が得られる可能性がありますが、そのためのライブラリを実際に紹介することはできません。

于 2008-10-07T15:23:22.987 に答える
1

私は最終的にこれに自分で答えることになりました。私にとっての解決策は、学校で行っていたことをコードで行うことでした。メソッドは次のように機能します。

  1. それぞれの数字を取り、小数点以下の桁数が同じであることを確認してください。したがって、 と を追加する場合2.13.457最初のものを に「正規化」し2.100ます。小数点以下の桁数を記録しておきます。この場合は 3 です。
  2. 小数点を削除し、 を使用してとmpz_addになった 2 つの数値を加算します。結果はです。210034575557
  3. 最後に、小数点を右から 3 文字 (この場合) 再挿入すると、 の正しい答えが得られ5.557ます。

VBScript でソリューションのプロトタイプを作成しました (以下)。

function fadd( n1, n2 )
    dim s1, s2, max, mul, res
    normalise3 n1, n2, s1, s2, max
    s1 = replace( s1, ".", "" )
    s2 = replace( s2, ".", "" )
    mul = clng(s1) + clng(s2)
    res = left( mul, len(mul) - max ) & "." & mid( mul, len( mul ) - max + 1 )
    fadd = res
end function

sub normalise3( byval n1, byval n2, byref s1, byref s2, byref numOfDigits )
    dim a1, a2
    dim max
    if instr( n1, "." ) = 0 then n1 = n1 & "."
    if instr( n2, "." ) = 0 then n2 = n2 & "."
    a1 = split( n1, "." )
    a2 = split( n2, "." )
    max = len( a1(1) )
    if len( a2(1) ) > max then max = len( a2( 1 ) )
    s1 = a1(0) & "." & a1(1) & string( max - len( a1( 1 )), "0" )
    s2 = a2(0) & "." & a2(1) & string( max - len( a2( 1 )), "0" )
    numOfDigits = max
end sub

最後に、Visual C++ (下記) を使用します。

#define Z(x) mpz_t x; mpz_init( x );

BSTR __stdcall FADD( BSTR p1, BSTR p2 ) {
  USES_CONVERSION;

  LPSTR sP1 = W2A( p1 );
  LPSTR sP2 = W2A( p2 );

  char LeftOf1[ 1024 ];
  char RightOf1[ 1024 ];
  char LeftOf2[ 1024 ];
  char RightOf2[ 1024 ];
  char * dotPos;
  long numOfDigits;
  int i;
  int amtOfZeroes;

  dotPos = strstr( sP1, "." );
  if ( dotPos == NULL ) {
    strcpy( LeftOf1, sP1 );
    *RightOf1 = '\0';
  } else {
    *dotPos = '\0';
    strcpy( LeftOf1, sP1 );
    strcpy( RightOf1, (dotPos + 1) );
  }

  dotPos = strstr( sP2, "." );
  if ( dotPos == NULL ) {
    strcpy( LeftOf2, sP2 );
    *RightOf2 = '\0';
  } else {
    *dotPos = '\0';
    strcpy( LeftOf2, sP2 );
    strcpy( RightOf2, (dotPos + 1) );
  }

  numOfDigits = strlen( RightOf1 ) > strlen( RightOf2 ) ? strlen( RightOf1 ) : strlen( RightOf2 );

  strcpy( sP1, LeftOf1 );
  strcat( sP1, RightOf1 );
  amtOfZeroes = numOfDigits - strlen( RightOf1 );
  for ( i = 0; i < amtOfZeroes; i++ ) {
    strcat( sP1, "0" );
  }
  strcpy( sP2, LeftOf2 );
  strcat( sP2, RightOf2 );
  amtOfZeroes = numOfDigits - strlen( RightOf2 );
  for ( i = 0; i < amtOfZeroes; i++ ) {
    strcat( sP2, "0" );
  }


  Z(n1);
  Z(n2);
  Z(res);

  mpz_set_str( n1, sP1, 10 );
  mpz_set_str( n2, sP2, 10 );
  mpz_add( res, n1, n2 );

  char * buff =  (char *) _alloca( mpz_sizeinbase( res, 10 ) + 2 + 1 );

  mpz_get_str(buff, 10, res);

  char * here = buff + strlen(buff) - numOfDigits; 

  memmove( here + 1, here, strlen(buff)); // plus trailing null
  *(here) = '.';

  BSTR bResult = _com_util::ConvertStringToBSTR( buff );
  return bResult;
}

私はCが少し...うーん...危険であることを認めますので、お気軽に批評してください。すべての有益なコメントに感謝します。

ここから、FSUB と FMUL も実装しました。FDIV はそれほど満足のいくものではなく、最終的に 3 つのバージョンになり、有理数を使用しました。

于 2008-10-08T18:28:44.157 に答える