如何从小数获取分子和分母?

时间:2018-06-21 06:47:02

标签: c++

如何从小数获取分子和分母?例如,我希望从“ 1.375”得到“ 1375/1000”或“ 11/8”。我如何用C ++做到这一点? 我试图通过将点之前和之后的数字分开来做到这一点,但是它并不清楚如何获得所需的输出。

5 个答案:

答案 0 :(得分:1)

在C ++中,您可以使用Boost有理类。但是您需要给出分子和分母。

为此,您需要找出小数点后的输入字符串中没有数字。您可以通过字符串操作函数来执行此操作。逐字符读取输入的字符,并在.

之后查找没有字符
char inputstr[30]; 
int noint=0, nodec=0;
char intstr[30], dec[30];
int decimalfound = 0;
int denominator = 1;
int numerator;

scanf("%s",inputstr);

len = strlen(inputstr);

for (int i=0; i<len; i++)
{
    if (decimalfound ==0)
    {
        if (inputstr[i] == '.')
        {
            decimalfound = 1;
        }
        else
        {
            intstr[noint++] = inputstr[i];
        }
     }
     else
     {
        dec[nodec++] = inputstr[i];
        denominator *=10;
     }
}
dec[nodec] = '\0';
intstr[noint] = '\0';

numerator = atoi(dec) + (atoi(intstr) * 1000);

// You can now use the numerator and denominator as the fraction, 
// either in the Rational class or you can find gcd and divide by 
// gcd.

答案 1 :(得分:0)

我希望发布一个仅使用“ C语言”的答案得到原谅。我知道您用C++标记了这个问题-但抱歉,我无法拒绝。至少C++仍然有效(尽管它确实主要使用C字符串处理技术)。

int num_string_float_to_rat(char *input, long *num, long *den) {
    char *tok = NULL, *end = NULL;
    char buf[128] = {'\0'};
    long a = 0, b = 0;
    int den_power = 1;

    strncpy(buf, input, sizeof(buf) - 1);

    tok = strtok(buf, ".");
    if (!tok) return 1;
    a = strtol(tok, &end, 10);
    if (*end != '\0') return 2;
    tok = strtok(NULL, ".");
    if (!tok) return 1;
    den_power = strlen(tok);  // Denominator power of 10
    b = strtol(tok, &end, 10);
    if (*end != '\0') return 2;

    *den = static_cast<int>(pow(10.00, den_power));
    *num = a * *den + b;

    num_simple_fraction(num, den);

    return 0;
}

示例用法:

int rc = num_string_float_to_rat("0015.0235", &num, &den);
// Check return code -> should be 0!
printf("%ld/%ld\n", num, den);

输出:

30047/2000

http://codepad.org/CFQQEZkc处的完整示例。

注释:

  • strtok()用于将输入解析为令牌(在这方面无需重新发明轮子)。 strtok()修改其输入-因此为了安全起见,使用了临时缓冲区
  • 它检查无效字符-如果找到,将返回非零的返回码
  • 使用strtol()代替atoi()-因为它可以检测输入中的非数字字符
  • 由于浮点数的舍入问题,
  • scanf()未使用 进行输入处理
  • strtol()的基数已明确设置为10,以避免出现前导零的问题(否则前导零将导致数字被解释为八进制)
  • 它使用num_simple_fraction()帮助程序(未显示)-反过来使用gcd()帮助程序(也未显示)-将结果转换为简单的分数
  • 分子的
  • log10()是通过计算小数点后的令牌长度来确定的

答案 2 :(得分:0)

这个简单的代码呢?

double n = 1.375;
int num = 1, den = 1;
double frac = (num * 1.f / den);
double margin = 0.000001;
while (abs(frac - n) > margin){
    if (frac > n){
        den++;
    }
    else{
        num++;
    }
    frac = (num * 1.f / den);
}

我并没有进行太多测试,这只是一个主意。

答案 3 :(得分:0)

我将分三个步骤进行操作。

1)找到小数点,以便知道分母必须多大。

2)获取分子。那只是去除小数点的原始文本。

3)获得分母。如果没有小数点,则分母为1。否则,分母为10 ^ n,其中n是(已删除的)小数点右边的位数。

WebView webview = new WebView(this);
        setContentView(webview);
        webview.loadUrl("https://myurl.com/");
        webview.setWebViewClient(new MyWebViewClient());


private class MyWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            Toast.makeText(LoginActivityWebView.this, url, Toast.LENGTH_SHORT).show();
                return false;

        }
    }

答案 4 :(得分:0)

您并没有真正指定是否需要将浮点数或字符串转换为比率,所以我将假设是前一个。

您可以直接使用IEEE-754编码的属性,而不必尝试使用基于字符串或基于算术的方法。

浮点数(按标准称为binary32)在内存中的编码方式如下:

 S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM
 ^                                ^
bit 31                           bit 0

其中S是符号位,E是指数位(其中8个)M是尾数位(23位)。

数字可以这样解码:

value = (-1)^S * significand * 2 ^ expoenent

where:
    significand = 1.MMMMMMMMMMMMMMMMMMMMMMM (as binary)
    exponent = EEEEEEEE (as binary) - 127

(注意:这是所谓的“正常数”,也有零,次正规数,无穷大和NaN-参见我链接的维基百科页面)

可以在这里使用。我们可以这样重写上面的等式:

(-1)^S * significand * exponent = (-1)^s * (significand * 2^23) * 2 ^ (exponent - 23)

问题在于significand * 2^23是一个整数(等于1.MMMMMMMMMMMMMMMMMMMMMMM,二进制-通过乘以2 ^ 23,我们将点右移了23位)。2 ^ (exponent - 23)是一个整数显然也是。

换句话说:我们可以将数字写为:

(significand * 2^23) / 2^(-(exponent - 23))    (when exponent - 23 < 0)
or
[(significand * 2^23) * 2^(exponent - 23)] / 1 (when exponent - 23 >= 0)

所以我们既有分子又有分母-直接来自数字的二进制表示形式。


以上所有内容都可以在C ++中实现:

struct Ratio
{
    int64_t numerator; // numerator includes sign
    uint64_t denominator;

    float toFloat() const
    {
        return static_cast<float>(numerator) / denominator;
    }

    static Ratio fromFloat(float v)
    {
        // First, obtain bitwise representation of the value
        const uint32_t bitwiseRepr = *reinterpret_cast<uint32_t*>(&v);

        // Extract sign, exponent and mantissa bits (as stored in memory) for convenience:
        const uint32_t signBit = bitwiseRepr >> 31u;
        const uint32_t expBits = (bitwiseRepr >> 23u) & 0xffu; // 8 bits set
        const uint32_t mntsBits = bitwiseRepr & 0x7fffffu; // 23 bits set

        // Handle some special cases:
        if(expBits == 0 && mntsBits == 0)
        {
            // special case: +0 and -0
            return {0, 1};
        }
        else if(expBits == 255u && mntsBits == 0)
        {
            // special case: +inf, -inf
            // Let's agree that infinity is always represented as 1/0 in Ratio 
            return {signBit ? -1 : 1, 0};
        }
        else if(expBits == 255u)
        {
            // special case: nan
            // Let's agree, that if we get NaN, we returns max int64_t by 0
            return {std::numeric_limits<int64_t>::max(), 0};
        }

        // mask lowest 23 bits (mantissa)
        uint32_t significand = (1u << 23u) | mntsBits;

        const int64_t signFactor = signBit ? -1 : 1;

        const int32_t exp = expBits - 127 - 23;

        if(exp < 0)
        {
            return {signFactor * static_cast<int64_t>(significand), 1u << static_cast<uint32_t>(-exp)};
        }
        else
        {
            return {signFactor * static_cast<int64_t>(significand * (1u << static_cast<uint32_t>(exp))), 1};
        }
    }
};

(希望上面的评论和描述是可以理解的,如果有需要改进的地方,请告诉我)

为简单起见,我省略了检查超出范围的值。

我们可以这样使用它:

float fv = 1.375f;
Ratio rv = Ratio::fromFloat(fv);
std::cout << "fv = " << fv << ", rv = " << rv << ", rv.toFloat() = " << rv.toFloat() << "\n";

输出为:

  

fv = 1.375,rv = 11534336/8388608,rv.toFloat()= 1.375

如您所见,两端的值完全相同。


问题在于分子和分子的数量很大。这是因为代码总是将有效数乘以2 ^ 23,即使较小的值足以使它成为整数(这等同于将0.2写为2000000/10000000而不是2/10-这是同一件事,只是写法不同)

这可以通过更改代码以将有效数(除以指数)乘以最小数来解决,如下所示(省略号代表与上述相同的部分):

// counts number of subsequent least significant bits equal to 0
// example: for 1001000 (binary) returns 3
uint32_t countTrailingZeroes(uint32_t v)
{
    uint32_t counter = 0;

    while(counter < 32 && (v & 1u) == 0)
    {
        v >>= 1u;
        ++counter;
    }

    return counter;
}

struct Ratio
{
    ...

    static Ratio fromFloat(float v)
    {
        ... 

        uint32_t significand = (1u << 23u) | mntsBits;

        const uint32_t nTrailingZeroes = countTrailingZeroes(significand);
        significand >>= nTrailingZeroes;

        const int64_t signFactor = signBit ? -1 : 1;

        const int32_t exp = expBits - 127 - 23 + nTrailingZeroes;

        if(exp < 0)
        {
            return {signFactor * static_cast<int64_t>(significand), 1u << static_cast<uint32_t>(-exp)};
        }
        else
        {
            return {signFactor * static_cast<int64_t>(significand * (1u << static_cast<uint32_t>(exp))), 1};
        }
    }
};

现在,对于以下代码:

float fv = 1.375f;
Ratio rv = Ratio::fromFloat(fv);

std::cout << "fv = " << fv << ", rv = " << rv << ", rv.toFloat() = " << rv.toFloat() << "\n";

我们得到:

  

fv = 1.375,rv = 11/8,rv.toFloat()= 1.375