2008-10-07 85 views
1

在下面的代码中,我使用mpf_add来添加两个浮点值的字符串表示形式。我现在不明白的是为什么2.2 + 3.2 = 5.39999999999999999999999999999999999999。我本以为gmp足够聪明,可以给5.4用gmp添加浮点数给出“正确”结果,排序

我不理解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; 
} 
+0

最简单的就是使用mpq_t,对有理数的操作是确切的。 – 2013-01-09 15:18:34

回答

4

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

有关更多信息,请参阅IEEE 754标准。

1

什么warrensaid

如果您使用二进制编码的十进制代替浮点数,您可能会得到更好的结果,但我无法真正指引您访问任何库。

1

我最终最终自己回答了这个问题。对我来说,解决方案是用代码完成我以前在学校做的事情。该方法是这样的:

  1. 每次取号,并确保数字小数点右边的数字是相同的。因此,如果加入2.13.457,则将第一个“正常化”为2.100。记录小数点右边的位数,在本例中为三位。
  2. 现在删除小数点并使用mpz_add添加两个数字,现在变成21003457。结果是5557
  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几乎没有那么令人满意,最终以三个版本并使用有理数。