Модераторы: bsa

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Преобразование вещественной части в целое 
:(
    Опции темы
den140387
Дата 20.8.2013, 11:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 21.12.2011

Репутация: нет
Всего: нет



Добрый День!
Прошу помочь в написании кода преобразовании вещественной части long double в целочисленное значение.

То есть должно быть что-то типа:
long double ld = 123.456;
int c = func( ld); // c = 456

Проблема в том, чтобы написать эту процедуру func.
Число типа long double пользователь должен ввести с клавиатуры.
Точность заранее неизвестна.

Пробывал написать след. образом:
Код

#include <iostream>
using std :: cin;

int main( void)
{
     // Ввод вещественного значения типа long double
     long double ld;
     cin >> ld;

     // Целая часть
     int const ival = static_cast<int> (ld);

     // Вещественная часть
     long double ldval = ld - ival;

     // Преобразование вещественной части в целое
     while ( ldval != static_cast<int> (ldval) )
              ldval *= 10;

     return 0;
} /* main */


Задумка моя в том, чтобы каждый раз умножать на 10 до тех пор,
пока плавающая часть не станет равной 0.

То есть, если пользователь ввел: ld = 12.34, то ldval = 0.34
Далее в цикле ldval = 0.34, 3.4, 34.00

Но проблема в том, что 34.00 и 34 - это не два одинаковых числа, то есть разница
между ними не равна 0.  

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

Просьба помочь в решении данной задачи.
Может быть есть какая-то константа, позволяющая сравнивать на равенство целочисленные и вещественные типы?
Прежде чем задать вопрос я погуглить, но ничего полезного не нашел..
PM MAIL   Вверх
SenkraD
Дата 20.8.2013, 11:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 933
Регистрация: 3.2.2006
Где: Украина::Киев

Репутация: нет
Всего: 23



Ну вы же читали про вещественные типы smile, а там настоятельно рекомендуют сравнивать с заданой точностью тоесть как-то так:
Код

long long_value = 34;
long double double_value= 34.00;
if ((double_value - long_value) <= 0.001)
{
    std::cout << "values are equal" << std::endl;
}



--------------------
 Имеющий язык - да не убоится спросить! 
user posted image
PM MAIL ICQ   Вверх
den140387
  Дата 20.8.2013, 11:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 21.12.2011

Репутация: нет
Всего: нет



Цитата(SenkraD @ 20.8.2013,  11:10)
Ну вы же читали про вещественные типы smile, а там настоятельно рекомендуют сравнивать с заданой точностью тоесть как-то так:
Код

long long_value = 34;
long double double_value= 34.00;
if ((double_value - long_value) <= 0.001)
{
    std::cout << "values are equal" << std::endl;
}

Спасибо! smile 
Я читал про них в Лафоре и Липпмане. 
Там говорится только то, что long double имеет точность в 19 цифр, если я правильно помню.
В связи с этим вопрос: почему вы выбрали именно "0.001", а не "0.01" или же "0.00001"?
Раз точность 19 цифр, то по идее 18-ть нулей должно быть, если я ничего не путаю..


PM MAIL   Вверх
feodorv
Дата 20.8.2013, 11:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Цитата(den140387 @  20.8.2013,  12:01 Найти цитируемый пост)
Я прочитал про вещественные и целочисленные типы, понимаю, почему они не равны

Так, для понимания Вашего понимания, почему?


Цитата(den140387 @  20.8.2013,  12:01 Найти цитируемый пост)
преобразовании вещественной части long double

А что такое "вещественная часть long double"? Дробная часть? Тогда в общем случае задача не имеет (правильного) решения.


Цитата(den140387 @  20.8.2013,  12:01 Найти цитируемый пост)
То есть должно быть что-то типа:
long double ld = 123.456;
int c = func( ld); // c = 456


Ну, код, который Вы привели, несколько отличается от этого:

Цитата(den140387 @  20.8.2013,  12:01 Найти цитируемый пост)
     // Ввод вещественного значения типа long double
     long double ld;
     cin >> ld;


Отличие именно в том, что значение для long double не задано, а вводится с клавиатуры. Тогда задача имеет решение, ибо можно вводить его не сразу в long double, а в строку.
Затем эту строку можно проанализировать на тему, что это действительно строковое представление вещественного числа. А уже потом найти точку и вычислить искомое (с проверкой на переполнение).





--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
den140387
Дата 20.8.2013, 11:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 21.12.2011

Репутация: нет
Всего: нет



Цитата(feodorv @ 20.8.2013,  11:18)
Цитата(den140387 @  20.8.2013,  12:01 Найти цитируемый пост)
Я прочитал про вещественные и целочисленные типы, понимаю, почему они не равны

Так, для понимания Вашего понимания, почему?


Цитата(den140387 @  20.8.2013,  12:01 Найти цитируемый пост)
преобразовании вещественной части long double

А что такое "вещественная часть long double"? Дробная часть? Тогда в общем случае задача не имеет (правильного) решения.


Цитата(den140387 @  20.8.2013,  12:01 Найти цитируемый пост)
То есть должно быть что-то типа:
long double ld = 123.456;
int c = func( ld); // c = 456


Ну, код, который Вы привели, несколько отличается от этого:

Цитата(den140387 @  20.8.2013,  12:01 Найти цитируемый пост)
     // Ввод вещественного значения типа long double
     long double ld;
     cin >> ld;


Отличие именно в том, что значение для long double не задано, а вводится с клавиатуры. Тогда задача имеет решение, ибо можно вводить его не сразу в long double, а в строку.
Затем эту строку можно проанализировать на тему, что это действительно строковое представление вещественного числа. А уже потом найти точку и вычислить искомое (с проверкой на переполнение).

Цитата

Так, для понимания Вашего понимания, почему?

Как я понимаю, они просто по-другому в памяти машины представляются.
Для плавающих есть мантисса и экспонента, а для целочисленных - нет

Цитата

А что такое "вещественная часть long double"? Дробная часть? Тогда в общем случае задача не имеет (правильного) решения.

Ну как я понимаю, дробная часть с точностью до 19ти цифр.
А почему не имеет решения? Мне же известна максимальная точность long double: 19 цифр после запятой.

Цитата

Отличие именно в том, что значение для long double не задано, а вводится с клавиатуры. Тогда задача имеет решение, ибо можно вводить его не сразу в long double, а в строку.
Затем эту строку можно проанализировать на тему, что это действительно строковое представление вещественного числа. А уже потом найти точку и вычислить искомое (с проверкой на переполнение).

По поводу строки я уже сделал.
Было две задачи: 
1) Пользователь вводит строку.
Необходимо вывести это же значение в формате long double
Это я сделал.

2) Пользователь вводит число типа long double.
Оно должно быть прочитано как long double.
Необходимо вывести его в виде строки.
Целую часть в строку я преобразовал.
А вот с дробной возникла проблема.



PM MAIL   Вверх
SenkraD
Дата 20.8.2013, 11:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 933
Регистрация: 3.2.2006
Где: Украина::Киев

Репутация: нет
Всего: 23



Цитата(den140387 @  20.8.2013,  11:17 Найти цитируемый пост)
В связи с этим вопрос: почему вы выбрали именно "0.001", а не "0.01" или же "0.00001"?
тут уже от задачи зависит, а вам наверное подойдет std::numeric_limits<long double>::epsilon(). вот пример с msdn
Код
#include <iostream>
#include <limits>

using namespace std;

int main( )
{
    cout << "The difference between 1 and the smallest value greater than 1\n for float objects is: " 
            << numeric_limits<float>::epsilon( ) 
            << endl;
    cout << "The difference between 1 and the smallest value greater than 1\n for double objects is: " 
            << numeric_limits<double>::epsilon( ) 
            << endl;
    cout << "The difference between 1 and the smallest value greater than 1\n for long double objects is: " 
            << numeric_limits<long double>::epsilon( ) 
            << endl;
}

Код

The difference between 1 and the smallest value greater than 1 for float objects is: 1.19209e-007
The difference between 1 and the smallest value greater than 1 for double objects is: 2.22045e-016
The difference between 1 and the smallest value greater than 1 for long double objects is: 2.22045e-016



--------------------
 Имеющий язык - да не убоится спросить! 
user posted image
PM MAIL ICQ   Вверх
feodorv
Дата 20.8.2013, 12:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Цитата(den140387 @  20.8.2013,  12:35 Найти цитируемый пост)
Необходимо вывести его в виде строки.

Простите, но это совсем другая задача. Не нужно её сводить к нахождению дробной части в виде беззнакового целого.
Да и целая часть long double не обязана умещаться в int или long long. Что Вы будете делать с числом 10^100?


Задача (адекватного!) вывода значения double или long double не так проста. Но если требуется простое решение, то, действительно, можно ограничиться определённым количеством знаков (после запятой). Но при этом совсем не нужно представлять целую и дробную части в виде int.


Как бы Вы решили задачу строкового представления int? Вот аналогично решается и задача, стоящая перед Вами.

Добавлено через 9 минут и 13 секунд
Цитата(den140387 @  20.8.2013,  12:35 Найти цитируемый пост)
А почему не имеет решения? Мне же известна максимальная точность long double: 19 цифр после запятой.

В какую целочисленную переменную типа int Вы собрались запихивать 19 знаков?

Цитата(den140387 @  20.8.2013,  12:35 Найти цитируемый пост)
Как я понимаю, они просто по-другому в памяти машины представляются.

Проблема не в этом, а в том, что числа с плавающей точкой имеют двоичное машинное представление. То есть деление на два не разрушает мантиссу, а вот деление на 10 уже даёт приблизительный результат (то есть x / 10 * 10 может уже не равняться x). А мы хотим иметь именно 10тичное строковое представление, которое, увы, не обязано совпадать с нашими ожиданиями (вместо .34 может получиться .33999989889...)


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
akizelokro
Дата 20.8.2013, 14:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Крокодил
**


Профиль
Группа: Участник
Сообщений: 761
Регистрация: 30.7.2007

Репутация: 1
Всего: 5



Первоначально у нас вводится строковый тип. Так и работайте с ним. Предположим, что точка в вашей текущей локали разделитель целой и дробной части

Код

tstring tstr;
tcin >> tstr;
// здесь добавить проверку на то, что вообще введено число, а не эники-бэники
int pos = tstr.find(_T("."));
if(pos != -1)
{
      tstr = tstr.substr(pos + 1, tstr.length());
      size_type sz = 0;
      long long result = strtoll(tstr, &sz, 0);
}
else
     tcout << _T("Алгоритмисты, блин!");


Это в предположении, что ввод в десятичной системе, но без "E-2", например.



Это сообщение отредактировал(а) akizelokro - 20.8.2013, 14:16


--------------------
a = a + b; b = a - b; a = a - b;
PM MAIL   Вверх
akizelokro
Дата 20.8.2013, 14:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Крокодил
**


Профиль
Группа: Участник
Сообщений: 761
Регистрация: 30.7.2007

Репутация: 1
Всего: 5



Цитата

Было две задачи: 
1) Пользователь вводит строку.
Необходимо вывести это же значение в формате long double
Это я сделал.

2) Пользователь вводит число типа long double.
Оно должно быть прочитано как long double.
Необходимо вывести его в виде строки.
Целую часть в строку я преобразовал.
А вот с дробной возникла проблема.


Вообще-то, он в любом случае вводит строку, если считать для упрощения.
Есть в стандарте функции to_tstring (на самом деле to_string и to_wstring, но мне сподручней писать сразу to_tstring)
И разные функции обратного преобразования из строки в числовые значения stol, stoi, stod ..

Кажисть, правда в С++11 стандарте. но эти функции уже и студия даже поддерживает

Это сообщение отредактировал(а) akizelokro - 20.8.2013, 14:26


--------------------
a = a + b; b = a - b; a = a - b;
PM MAIL   Вверх
akizelokro
Дата 20.8.2013, 14:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Крокодил
**


Профиль
Группа: Участник
Сообщений: 761
Регистрация: 30.7.2007

Репутация: 1
Всего: 5



а если проблема с преобразованием вещественной части и нельзя прибегать к стандартным этим функция преобразования, то берёте вещественную часть и по циклу умножаете каждый раз на 10, новая целая часть даёт очередную цифру вещественной части. Её пишете и целую часть отбрасываете, и снова повтор умножения на 10. И там до тех пор пока результат не станет равен нулю.


--------------------
a = a + b; b = a - b; a = a - b;
PM MAIL   Вверх
feodorv
Дата 20.8.2013, 17:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 12
Всего: 45



Цитата(akizelokro @  20.8.2013,  15:43 Найти цитируемый пост)
Её пишете и целую часть отбрасываете, и снова повтор умножения на 10.

Всё так. Но:
Цитата(akizelokro @  20.8.2013,  15:43 Найти цитируемый пост)
 И там до тех пор пока результат не станет равен нулю.

Результат может никогда не стать равным нулю...



--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
den140387
Дата 20.8.2013, 19:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 21.12.2011

Репутация: нет
Всего: нет



Цитата

тут уже от задачи зависит, а вам наверное подойдет std::numeric_limits<long double>::epsilon(). вот пример с msdn

Спасибо! smile

Добавлено через 5 минут и 5 секунд
Цитата(feodorv @ 20.8.2013,  12:13)
Цитата(den140387 @  20.8.2013,  12:35 Найти цитируемый пост)
Необходимо вывести его в виде строки.

Простите, но это совсем другая задача. Не нужно её сводить к нахождению дробной части в виде беззнакового целого.
Да и целая часть long double не обязана умещаться в int или long long. Что Вы будете делать с числом 10^100?


Задача (адекватного!) вывода значения double или long double не так проста. Но если требуется простое решение, то, действительно, можно ограничиться определённым количеством знаков (после запятой). Но при этом совсем не нужно представлять целую и дробную части в виде int.


Как бы Вы решили задачу строкового представления int? Вот аналогично решается и задача, стоящая перед Вами.

Добавлено @ 12:23
Цитата(den140387 @  20.8.2013,  12:35 Найти цитируемый пост)
А почему не имеет решения? Мне же известна максимальная точность long double: 19 цифр после запятой.

В какую целочисленную переменную типа int Вы собрались запихивать 19 знаков?

Цитата(den140387 @  20.8.2013,  12:35 Найти цитируемый пост)
Как я понимаю, они просто по-другому в памяти машины представляются.

Проблема не в этом, а в том, что числа с плавающей точкой имеют двоичное машинное представление. То есть деление на два не разрушает мантиссу, а вот деление на 10 уже даёт приблизительный результат (то есть x / 10 * 10 может уже не равняться x). А мы хотим иметь именно 10тичное строковое представление, которое, увы, не обязано совпадать с нашими ожиданиями (вместо .34 может получиться .33999989889...)

Цитата

Как бы Вы решили задачу строкового представления int? Вот аналогично решается и задача, стоящая перед Вами.

c int - все просто. Я могу легко пройтись по всему целому числу и каждую цифру преобразовать в символ, путем
сложения с '0'. В случае обхода целочисленного значения, я могу легко получить условие того, что обработал
все цифры этого значения. А вот с вещественными типами проблема из-за того, что я не знаю, когда заканчивается
это число.

Добавлено через 7 минут и 26 секунд
Цитата

Результат может никогда не стать равным нулю..

+1.
В способе, который я привел в самом начале, происходило зацикливание.

PM MAIL   Вверх
akizelokro
Дата 20.8.2013, 19:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Крокодил
**


Профиль
Группа: Участник
Сообщений: 761
Регистрация: 30.7.2007

Репутация: 1
Всего: 5



Цитата(feodorv @  20.8.2013,  17:43 Найти цитируемый пост)
Результат может никогда не стать равным нулю...


Неочевидно. Десять в минус какой степени там предел? tstring схавает. А если уж кому-то интересно, то можно break на пятитысячной итерации поставить.





--------------------
a = a + b; b = a - b; a = a - b;
PM MAIL   Вверх
den140387
Дата 20.8.2013, 19:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 21.12.2011

Репутация: нет
Всего: нет



Цитата

Проблема не в этом, а в том, что числа с плавающей точкой имеют двоичное машинное представление. То есть деление на два не разрушает мантиссу, а вот деление на 10 уже даёт приблизительный результат (то есть x / 10 * 10 может уже не равняться x). А мы хотим иметь именно 10тичное строковое представление, которое, увы, не обязано совпадать с нашими ожиданиями (вместо .34 может получиться .33999989889...) 

Спасибо!  smile 
Вы гораздо более понятно объяснили, чем в книжке.
PM MAIL   Вверх
akizelokro
Дата 20.8.2013, 20:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Крокодил
**


Профиль
Группа: Участник
Сообщений: 761
Регистрация: 30.7.2007

Репутация: 1
Всего: 5



Цитата(den140387 @  20.8.2013,  19:21 Найти цитируемый пост)
В способе, который я привел в самом начале, происходило зацикливание.


Это где вы делали сравнение double с интом?
Ну-ну! Там вообще с вашим умножением на 10 в цикле может быть неизвестно что. Об этом вам писали уже. 
Вам надо было сравнивать именно с нулём (значение double), постоянно отсекая целую часть (появившуюся цифру), перенося её в строку. 
и там уже не нужны никакие ограничения на размер инта. Работа ведётся только с double и string.

А привлекать ещё дополнительный тип, когда можно обойтись и без него, нерационально.
Единственное, что его можно использовать для того, чтобы цифру, появлюящуюся после умножения на 10 вещественной части преобразовывать в литерал (строку).
Есть разница между 

Код

while ( ldval != static_cast<int> (ldval) )
              ldval *= 10;


и
Код

while ( ldval != 0 )
{
              ldval *= 10;
              // взять целочисленную часть, образовавшуюся после умножения цифру и выдрать её в string
}


А вот это именно и есть разница в подходах. Бритва Оккама. 
О чем и был мой первый пример кода. string может захавать сколько угодно цифр после запятой.


А проверку на NaN'ы и прочие эники-беники тоже реализуете

Добавлено через 29 секунд
И не надо ничего делить на десять.

Добавлено через 9 минут и 39 секунд
Просто double c int сравнивать никогда не надо. Так, совет на будущее. Потому что результат вообще неочевиден


--------------------
a = a + b; b = a - b; a = a - b;
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.1329 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.