2017-09-21 91 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; 
}