2017-09-21 84 views
0

我試圖從一個變量中提取20位小數,但應與除法運算錯誤,因爲這個節目給了我一個錯誤的結果:C++鴻溝小數錯誤

#include <iostream> 
#include <cmath> 
using namespace std; 
int fracpart(long double input) 
{ 
    long long I; 
    I = input * 10; 
    return I % 10; 
} 

int main() 
{ 
    int n = 9, m = 450; 
    long double S; 
    S = (long double)n/m; 
    for(int i=1; i<=20; i++){ 
     cout << fracpart(S) << " "; 
     S *= 10; 
    } 
    return 0; 
} 

我得到什麼:

0 1 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 

我應該得到:

0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
+2

這看起來像它可能的副本[?是浮點運算破(https://stackoverflow.com/questions/588004/is-floating-point儘管我承認我並不完全確定代碼中發生了什麼事情。 – Carcigenicate

+0

我試圖讓一個程序從兩個數字的分割中得到20位小數。 –

回答

-1

我認爲這已經發生的事情「,因爲二元劃分不完全可轉換爲十進制數「,但Bob__是對的!問題正在發生,因爲long long變量是一種「問題」。所以,我只是改變了代碼,並使用了我提到的ceil回合的功能。這次我測試了代碼,所以我希望它能夠滿足您的需求。

PS1:提取函數是非常必要的。

PS2:不要忘記包含math.h庫。

PS3:並且,對於延遲迴答抱歉。

#include <iostream> 
#include <cmath> 
#include <math.h> 
using namespace std; 

int main() 
{ 
    int n = 9, m = 450; 
    long double S; 
    S = (long double)n/m; 
    for(int i=1; i<=20; i++){ 
     cout << fmod(round(fmod(S * 10,10)), 10) << " "; 
     S *= 10; 
    } 
    return 0; 
} 

下面是一些例子:http://www.cplusplus.com/reference/cmath/trunc/

+0

如果我使用trunc,那麼我只得到0 –

+0

我將編輯我的答案,使用'ceil()'是更好的選擇。嘗試一下。 –

+2

爲什麼不[std :: round](http://en.cppreference.com/w/cpp/numeric/math/round)?順便說一下,這是內部二進制浮點表示「有問題」,而不是分區。見例如http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html –

2

可以檢查通過浮點類型檢查宏恆定FLT_RADIX的值的表示所用的鹼(在報頭<cfloat>定義)。正如你可能已經知道的那樣,二進制系統被大多數現代計算機內部使用,而不是十進制。

現在考慮一個像1/3的有理數。它不能用基數爲10的有限數字表示,最終會出現一些近似值,如0.3333333和可接受的錯誤。請注意,相同的數字可以用有限數量的數字(0.1)在基本3系統中表示。

您嘗試打印的數字9/450具有「好」的基數10表示,0.02,但它不能以絕對精度表示在基數2中,即使可以在不添加分割的情況下執行分割任何錯誤。不要誤以爲'2',考慮0.02 = 2/100 = 1/50 = 1 /(2 * 5 ),其中1/5只能近似於基數2.

無論如何,有辦法實現你想要的,例如使用輸出操縱器std::setprecisionstd::fixed(在標頭<iomanip>中定義),或者甚至編寫(非常醜陋的)自定義函數。看看該程序的輸出:

#include <iostream> 
#include <cmath> 
#include <iomanip> 
#include <vector> 
#include <cstdint> 

// splits a number into its integral and fractional (a vector of digits) parts 
std::vector<uint8_t> to_digits (
    long double x, uint8_t precision, long double &integral 
); 

// Reconstructs the approximated number 
long double from_digits (
    long double integral_part, std::vector<uint8_t> &fractional_part 
); 

int main() 
{ 
    using std::cout; 

    int n = 9, m = 450; 
    long double S; 
    S = static_cast<long double>(n)/m; 

    cout << "\nBase 10 representation of calculated value:\n" 
     << std::setprecision(70) << S << '\n'; 
    // This ^^^^^^^^^^^^^^^^^^^^^ will change only how the value is 
    // printed, not its internal binary representation 

    cout << "\nBase 10 representation of literal:\n" 
     << 0.02L << '\n'; 
    // This ^^^^^ will print the exact same digits 

    // the next greater representable value is a worse approximation 
    cout << "\nNext representable value:\n" 
     << std::nextafter(S, 1.0) << '\n'; 

    // but you can try to obtain a "better" output 
    cout << "\nRounded representation printed using <iomanip> functions:\n" 
     << std::setprecision(20) << std::fixed << S << '\n'; 

    cout << "\nRounded fractional part printed using custom function:\n"; 
    long double integral_part; 
    auto dd = to_digits(S, 20, integral_part); 
    for (auto const d : dd) 
    { 
     cout << static_cast<int>(d); 
    } 
    cout << '\n'; 

    // Reversing the process... 
    cout << "\nApproximated value (using custom function):\n"; 
    auto X = from_digits(integral_part, dd); 
    cout << std::setprecision(70) << std::fixed << X << '\n'; 
    cout << std::setprecision(20) << std::fixed << X << '\n'; 
} 

std::vector<uint8_t> to_digits (
    long double x, uint8_t precision, long double &integral 
) 
{ 
    std::vector<uint8_t> digits; 

    long double fractional = std::modf(x, &integral); 

    for (uint8_t i = 0; i < precision; ++i) 
    { 
     long double digit; 
     fractional = std::modf(fractional * 10, &digit); 
     digits.push_back(digit); 
    } 

    if (digits.size() && std::round(fractional) == 1.0L) 
    { 
     uint8_t i = digits.size(); 
     while (i) 
     { 
      --i; 
      if (digits[i] < 9) 
      { 
       ++digits[i]; 
       break; 
      } 
      digits[i] = 0; 
      if (i == 0) 
      { 
       integral += 1.0L; 
       break; 
      } 
     } 
    } 

    return digits; 
} 

long double from_digits (
    long double integral_part, std::vector<uint8_t> &fractional_part 
) 
{ 
    long double x = 1.0L; 
    for (auto d : fractional_part) 
    { 
     x *= 10.0L; 
     integral_part += d/x; 
    } 

    return integral_part; 
}