使用gmp添加浮点数会产生“正确”的结果

时间:2008-10-07 15:14:39

标签: visual-c++ floating-point bignum gmp

在下面的代码中,我使用mpf_add添加两个浮点值的字符串表示。此时我不明白的是为什么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;
}

3 个答案:

答案 0 :(得分:4)

这是因为在二进制环境中使用浮点运算的固有错误。

有关详细信息,请参阅IEEE 754标准。

答案 1 :(得分:1)

warren said

如果您使用二进制编码的十进制而不是浮点数,您可能会得到更好的结果,尽管我无法真正将您引导到任何库。

答案 2 :(得分:1)

我最终自己也回答了这个问题。我的解决方案是在代码中做我以前在学校做的事情。该方法的工作方式如下:

  1. 取每个数字并确保小数点右边的位数相同。因此,如果添加2.13.457,请将第一个“标准化”为2.100。记录小数点右边的位数,在本例中为三位。
  2. 现在移除小数点并使用mpz_add添加两个数字,现在变为21003457。结果是5557
  3. 最后,从右侧重新插入小数点三个字符(在本例中),给出5.557的正确答案。
  4. 我在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不是那么令人满意,最终有三个版本并使用有理数字。

相关问题