C++ Преобразование восьмеричной дроби в десятичную дробь?

В настоящее время я работаю над программой, которая предназначена для ввода восьмеричной дроби и преобразования ее в десятичную дробь. Пока что у меня есть часть кода, которая преобразует часть до десятичной точки в десятичную, а не с плавающей запятой после десятичной точки. Я пытался использовать модуль, но безуспешно, потому что моя переменная была с плавающей запятой.

Есть ли способ преобразовать оставшиеся числа после запятой в десятичную из восьмеричной? Я разместил свой код ниже. Любая помощь приветствуется. Спасибо!

int main()
{
    float num;
    int rem = 0;;
    int dec = 0;
    int i = 0;

    cout << "Please enter a number with a decimal point: ";
    cin >> num;

    double ohalf = num - (int)num;
    int half = num;

    while (half != 0)
    {
        rem = half % 10;
        half /= 10; //Converts first have to decimal
        dec += rem *= pow(8, i);
        i++;
    }
    cout << dec;

    i = -1;
    while (ohalf != 0)
    {
        rem = ohalf *pow(8, i); //Converts second half to decimal. *This is where I am stuck*
        i--;
    }
    cout << rem;

    _getch();
    return 0;
}

person Justin Farr    schedule 10.10.2016    source источник
comment
Я думаю, было бы намного проще обрабатывать ввод как string, разделить его на символ '.', используйте strtol для разделенных строк, правильно масштабируйте дробную часть, затем добавьте их в double.   -  person Andrew Henle    schedule 10.10.2016


Ответы (3)


Следуя идее, что можно удалить десятичную точку, достаточно часто умножая на основание:

"123.456" in base 16 
=> BASE16("1234.56")/16 
=> BASE16("12345.6")/(16*16)
=> BASE16("123456")/(16*16*16)

or 

"123.456" in base 8
=> BASE8("1234.56")/8 
=> BASE8("12345.6")/(8*8)
=> BASE8("123456")/(8*8*8)

Итак, все, что нам нужно знать, это количество знаков после запятой. Затем мы можем удалить его и использовать std::stoi для преобразования оставшейся строки в нужную базу. В качестве последнего шага нам нужно снова разделить на base^number_of_places_after_decimal.

Собрав все вместе, вы получите что-то вроде этого:

#include <iostream>
#include <string>
#include <cmath>

using std::cout;
using std::cin;
using std::string;

int main()
{
    int base = 8;
    string value;

    cout << "Please enter a number with a decimal point: ";
    cin >> value;

    size_t ppos = value.find('.');
    if (ppos != string::npos) {
        value.replace(ppos,1,"");
    } else {
        ppos = value.size();
    }

    size_t mpos = 0;
    double dValue = (double)std::stoi(value, &mpos, base);
    if (mpos >= ppos)
    {
        dValue /= std::pow(base, (mpos - ppos));
    }

    std::cout << dValue << '\n';

    return 0;
}
person Simon Kraemer    schedule 10.10.2016
comment
Я думаю, вам лучше передать &maxp в качестве второго аргумента для std::stoi, чтобы конечные символы в строке не учитывались в мощности. - person Toby Speight; 10.10.2016
comment
P.S. Разве это не «восьмеричная точка», а не «десятичная точка»? :-п - person Toby Speight; 10.10.2016
comment
@TobySpeight соответствующим образом скорректировал мой ответ, спасибо, что указали на это. Я оставил decimal point в тексте, чтобы соответствовать вопросу :-P - person Simon Kraemer; 10.10.2016

Если вы уверены, что и целая, и дробная части вашего значения с плавающей запятой не превысят диапазон long long int, вы можете проанализировать обе части отдельно с помощью std::stoll(), а затем разделить дробную часть на соответствующую степень 8:

#include <cmath>
#include <stdexcept>
#include <string>

double parse_octal_fraction(const std::string s)
{
    std::size_t dotpos;
    auto whole = std::stoll(s, &dotpos, 8);
    if (dotpos+1 >= s.length())
        // no fractional part
        return whole;

    std::size_t fract_digits;
    auto frac = std::stoll(s.substr(dotpos+1), &fract_digits, 8);

    if (s.find_first_not_of("01234567", dotpos+1) != std::string::npos)
        throw std::invalid_argument("parse_octal_fraction");

    return whole + frac / std::pow(8, fract_digits);
}

#include <iostream>
int main()
{
    for (auto input: { "10", "0", "1.", "0.4", "0.04", "1.04", "1.04 ", "1. 04"})
        try {
            std::cout << input << " (octal) == " << parse_octal_fraction(input) << " (decimal)" << std::endl;
        } catch (const std::invalid_argument e) {
            std::cerr << "invalid input: " << e.what() << " " << input << std::endl;
        }
}

Показанные тестовые входные данные дают следующий результат:

10 (восьмеричное) == 8 (десятичное)
0 (восьмеричное) == 0 (десятичное)
1. (восьмеричное) == 1 (десятичное)
0,4 ​​(восьмеричное) == 0,5 (десятичное)
0,04 (восьмеричное) == 0,0625 (десятичное)
1,04 (восьмеричное) == 1,0625 (десятичное)
неверный ввод: parse_octal_fraction 1,04
неверный ввод: parse_octal_fraction 1. 04

person Toby Speight    schedule 10.10.2016

в вашем коде

 while (ohalf != 0)
{
    rem = ohalf *pow(8, i); //Converts second half to decimal. *This is where I am stuck*
    i--;
}

ohalf никогда не будет равно нулю, поэтому это может привести к бесконечному циклу

person Madhusudan chowdary    schedule 22.06.2017