![]() |
Модераторы: bsa |
![]() ![]() ![]() |
|
den140387 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 9 Регистрация: 21.12.2011 Репутация: нет Всего: нет |
Добрый День!
Прошу помочь в написании кода преобразовании вещественной части long double в целочисленное значение. То есть должно быть что-то типа: long double ld = 123.456; int c = func( ld); // c = 456 Проблема в том, чтобы написать эту процедуру func. Число типа long double пользователь должен ввести с клавиатуры. Точность заранее неизвестна. Пробывал написать след. образом:
Задумка моя в том, чтобы каждый раз умножать на 10 до тех пор, пока плавающая часть не станет равной 0. То есть, если пользователь ввел: ld = 12.34, то ldval = 0.34 Далее в цикле ldval = 0.34, 3.4, 34.00 Но проблема в том, что 34.00 и 34 - это не два одинаковых числа, то есть разница между ними не равна 0. Я прочитал про вещественные и целочисленные типы, понимаю, почему они не равны, но не представляю, как решить данную задачу. Просьба помочь в решении данной задачи. Может быть есть какая-то константа, позволяющая сравнивать на равенство целочисленные и вещественные типы? Прежде чем задать вопрос я погуглить, но ничего полезного не нашел.. |
|||
|
||||
SenkraD |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 933 Регистрация: 3.2.2006 Где: Украина::Киев Репутация: нет Всего: 23 |
Ну вы же читали про вещественные типы
![]()
|
|||
|
||||
den140387 |
|
||||
Новичок Профиль Группа: Участник Сообщений: 9 Регистрация: 21.12.2011 Репутация: нет Всего: нет |
Спасибо! ![]() Я читал про них в Лафоре и Липпмане. Там говорится только то, что long double имеет точность в 19 цифр, если я правильно помню. В связи с этим вопрос: почему вы выбрали именно "0.001", а не "0.01" или же "0.00001"? Раз точность 19 цифр, то по идее 18-ть нулей должно быть, если я ничего не путаю.. |
||||
|
|||||
feodorv |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 12 Всего: 45 |
Так, для понимания Вашего понимания, почему? А что такое "вещественная часть long double"? Дробная часть? Тогда в общем случае задача не имеет (правильного) решения.
Ну, код, который Вы привели, несколько отличается от этого:
Отличие именно в том, что значение для long double не задано, а вводится с клавиатуры. Тогда задача имеет решение, ибо можно вводить его не сразу в long double, а в строку. Затем эту строку можно проанализировать на тему, что это действительно строковое представление вещественного числа. А уже потом найти точку и вычислить искомое (с проверкой на переполнение). -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
||||||
|
|||||||
den140387 |
|
||||||
Новичок Профиль Группа: Участник Сообщений: 9 Регистрация: 21.12.2011 Репутация: нет Всего: нет |
Как я понимаю, они просто по-другому в памяти машины представляются. Для плавающих есть мантисса и экспонента, а для целочисленных - нет
Ну как я понимаю, дробная часть с точностью до 19ти цифр. А почему не имеет решения? Мне же известна максимальная точность long double: 19 цифр после запятой.
По поводу строки я уже сделал. Было две задачи: 1) Пользователь вводит строку. Необходимо вывести это же значение в формате long double Это я сделал. 2) Пользователь вводит число типа long double. Оно должно быть прочитано как long double. Необходимо вывести его в виде строки. Целую часть в строку я преобразовал. А вот с дробной возникла проблема. |
||||||
|
|||||||
SenkraD |
|
||||||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 933 Регистрация: 3.2.2006 Где: Украина::Киев Репутация: нет Всего: 23 |
|
||||||
|
|||||||
feodorv |
|
||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 12 Всего: 45 |
Простите, но это совсем другая задача. Не нужно её сводить к нахождению дробной части в виде беззнакового целого. Да и целая часть long double не обязана умещаться в int или long long. Что Вы будете делать с числом 10^100? Задача (адекватного!) вывода значения double или long double не так проста. Но если требуется простое решение, то, действительно, можно ограничиться определённым количеством знаков (после запятой). Но при этом совсем не нужно представлять целую и дробную части в виде int. Как бы Вы решили задачу строкового представления int? Вот аналогично решается и задача, стоящая перед Вами. Добавлено через 9 минут и 13 секунд
В какую целочисленную переменную типа int Вы собрались запихивать 19 знаков?
Проблема не в этом, а в том, что числа с плавающей точкой имеют двоичное машинное представление. То есть деление на два не разрушает мантиссу, а вот деление на 10 уже даёт приблизительный результат (то есть x / 10 * 10 может уже не равняться x). А мы хотим иметь именно 10тичное строковое представление, которое, увы, не обязано совпадать с нашими ожиданиями (вместо .34 может получиться .33999989889...) -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
||||
|
|||||
akizelokro |
|
|||
![]() Крокодил ![]() ![]() Профиль Группа: Участник Сообщений: 761 Регистрация: 30.7.2007 Репутация: 1 Всего: 5 |
Первоначально у нас вводится строковый тип. Так и работайте с ним. Предположим, что точка в вашей текущей локали разделитель целой и дробной части
Это в предположении, что ввод в десятичной системе, но без "E-2", например. Это сообщение отредактировал(а) akizelokro - 20.8.2013, 14:16 -------------------- a = a + b; b = a - b; a = a - b; |
|||
|
||||
akizelokro |
|
|||
![]() Крокодил ![]() ![]() Профиль Группа: Участник Сообщений: 761 Регистрация: 30.7.2007 Репутация: 1 Всего: 5 |
Вообще-то, он в любом случае вводит строку, если считать для упрощения. Есть в стандарте функции 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; |
|||
|
||||
akizelokro |
|
|||
![]() Крокодил ![]() ![]() Профиль Группа: Участник Сообщений: 761 Регистрация: 30.7.2007 Репутация: 1 Всего: 5 |
а если проблема с преобразованием вещественной части и нельзя прибегать к стандартным этим функция преобразования, то берёте вещественную часть и по циклу умножаете каждый раз на 10, новая целая часть даёт очередную цифру вещественной части. Её пишете и целую часть отбрасываете, и снова повтор умножения на 10. И там до тех пор пока результат не станет равен нулю.
-------------------- a = a + b; b = a - b; a = a - b; |
|||
|
||||
feodorv |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 12 Всего: 45 |
Всё так. Но: Результат может никогда не стать равным нулю... -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
|||
|
||||
den140387 |
|
||||||||||||
Новичок Профиль Группа: Участник Сообщений: 9 Регистрация: 21.12.2011 Репутация: нет Всего: нет |
Спасибо! ![]() Добавлено через 5 минут и 5 секунд
c int - все просто. Я могу легко пройтись по всему целому числу и каждую цифру преобразовать в символ, путем сложения с '0'. В случае обхода целочисленного значения, я могу легко получить условие того, что обработал все цифры этого значения. А вот с вещественными типами проблема из-за того, что я не знаю, когда заканчивается это число. Добавлено через 7 минут и 26 секунд
+1. В способе, который я привел в самом начале, происходило зацикливание. |
||||||||||||
|
|||||||||||||
akizelokro |
|
|||
![]() Крокодил ![]() ![]() Профиль Группа: Участник Сообщений: 761 Регистрация: 30.7.2007 Репутация: 1 Всего: 5 |
Неочевидно. Десять в минус какой степени там предел? tstring схавает. А если уж кому-то интересно, то можно break на пятитысячной итерации поставить. -------------------- a = a + b; b = a - b; a = a - b; |
|||
|
||||
den140387 |
|
|||
Новичок Профиль Группа: Участник Сообщений: 9 Регистрация: 21.12.2011 Репутация: нет Всего: нет |
Спасибо! ![]() Вы гораздо более понятно объяснили, чем в книжке. |
|||
|
||||
akizelokro |
|
||||||
![]() Крокодил ![]() ![]() Профиль Группа: Участник Сообщений: 761 Регистрация: 30.7.2007 Репутация: 1 Всего: 5 |
Это где вы делали сравнение double с интом? Ну-ну! Там вообще с вашим умножением на 10 в цикле может быть неизвестно что. Об этом вам писали уже. Вам надо было сравнивать именно с нулём (значение double), постоянно отсекая целую часть (появившуюся цифру), перенося её в строку. и там уже не нужны никакие ограничения на размер инта. Работа ведётся только с double и string. А привлекать ещё дополнительный тип, когда можно обойтись и без него, нерационально. Единственное, что его можно использовать для того, чтобы цифру, появлюящуюся после умножения на 10 вещественной части преобразовывать в литерал (строку). Есть разница между
и
А вот это именно и есть разница в подходах. Бритва Оккама. О чем и был мой первый пример кода. string может захавать сколько угодно цифр после запятой. А проверку на NaN'ы и прочие эники-беники тоже реализуете Добавлено через 29 секунд И не надо ничего делить на десять. Добавлено через 9 минут и 39 секунд Просто double c int сравнивать никогда не надо. Так, совет на будущее. Потому что результат вообще неочевиден -------------------- a = a + b; b = a - b; a = a - b; |
||||||
|
|||||||
feodorv |
|
||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 12 Всего: 45 |
Есть! Только я не понимаю, как умножая на 10 Вы собираетесь свести ldval к нулю:
По идее, с нулём нужно сравнивать дробную часть ldval:
На выходе получается что-то вроде
Ну, благодаря тому, что в числе 10 есть множитель двойка (а про двоичное машинное представление я уже говорил), предел, конечно, наступит. Каждое очередное умножение на двойку обнуляет очередной бит в хвосте мантиссы. Поскольку таких битов 63, то получаем максимальную итерацию - 63. Но если бы Вы захотели распечатать число, скажем, в 5-ричном виде, то конца бы не дождались))) На 10 происходит неявное деление, как только Вы пишите:
И вот тут машинное двоичное 0.456 не совпадает с нашим десятиричным 0.456 - чуть чуть, но отличается. PS Сравнение чисел с плавающей точкой с нулём, вообще говоря, не корректно. -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
||||||||
|
|||||||||
akizelokro |
|
||||||||||||
![]() Крокодил ![]() ![]() Профиль Группа: Участник Сообщений: 761 Регистрация: 30.7.2007 Репутация: 1 Всего: 5 |
комментарии пишутся для того, чтобы их читали
и почти это как раз вы сделали. (почему вы это сделали "почти", обяснение чуть ниже. не надо было изначально умножать в цикле на 10 до упора, не обнуляя целую часть, и всё это пытаться поместить в int'овскую переменную. и, кстати, из приведенного кода неочевидно, что
из-за куска в коде
здесь опять же идёт интересный момент double -= (double)(int)double поставьте число на вывод сразу после определения
конечно бы не дождался. такого варианта задачи я себе даже не ставил. точный же метод решения задачи был мной сразу приведён. сразу "хавать" введённое число с клавиатуры в string и парсить его. Добавлено @ 07:23 в принципе, можно при реализации алгоритм и подправить, учесть знак, умножить на "-1" например, если вообще интересует тогда уж строгое соответствие делает не сравнение, что не равно 0, а сравнение больше-равно нулю, отнимать не (int)х, а double-единицу в цикле пока число не станет меньше 1, чтобы не путаться лишний раз с приведением double к int и обратно. Мораль сего спича такова, вообщем. Если есть задача вывести целую или вещественную часть double-числа в строковый вид, то не надо налегать на int'ы. Это уже лишняя сущность и использовать её нужно желательно по минимуму, потому что в логике задачи не требуется. Это сообщение отредактировал(а) akizelokro - 21.8.2013, 07:26 -------------------- a = a + b; b = a - b; a = a - b; |
||||||||||||
|
|||||||||||||
feodorv |
|
||||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 12 Всего: 45 |
Моё (сугубо индивидуалистическое) видение решения задачи выглядит так:
Результаты получаются такого рода:
Что не хорошо (помимо артефактов деления на 10).
Что-то мне подсказывает, что >> не умеет читать long double, а только double... Да и вообще, long double лишь синоним double (это у Microsoft). -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
||||||||
|
|||||||||
feodorv |
|
|||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 12 Всего: 45 |
Прошу прощения, пропустил, увлёкся кодом))) Остальное ничего не понял(((
Если не нравится (double)(int)double (чем именно?) пользуйтесь modf. Что плохого в (double)(int)double? Ну и что, что лишнее приведение, главное, в точности потерь нет. Какие тонкости нужно выдерживать? Как именно их выдерживать - вычитать по 1.0??? И что это даст? Вы в курсе, что при этом будет задействован алгоритм, описанный ещё у Кнута, по "сглаживанию" вывода. То есть вывод != машинному представлению числа. -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
|||
|
||||
akizelokro |
|
||||
![]() Крокодил ![]() ![]() Профиль Группа: Участник Сообщений: 761 Регистрация: 30.7.2007 Репутация: 1 Всего: 5 |
Я просто не уверен, что в случае (double)(int)3.123 получится именно 3.0, а не опять очередная потеря точности Поэтому и не хочу применять преобразования double к int и обратно. Это к утверждению, почему возможно, что не аргумент в данном случае.
Да ничего там не будет такого. А будет там просто считывание ввода сразу в строку - tcin >> <строка>. Предположу, что как раз это и нужно автору темы (раз тема для новичков), а не подробные уже дебри. -------------------- a = a + b; b = a - b; a = a - b; |
||||
|
|||||
feodorv |
|
||||||
![]() Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2214 Регистрация: 30.7.2011 Репутация: 12 Всего: 45 |
Может, я чего не понимаю, но невидно никаких подводных камней по этому поводу. Число x - хорошее, не inf, не nan, < 10 и >= 1, откуда потеря точности?
![]() ![]()
Это сообщение отредактировал(а) feodorv - 21.8.2013, 11:08 -------------------- Напильник, велосипед, грабли и костыли - основные инструменты программиста... |
||||||
|
|||||||
akizelokro |
|
|||
![]() Крокодил ![]() ![]() Профиль Группа: Участник Сообщений: 761 Регистрация: 30.7.2007 Репутация: 1 Всего: 5 |
Давай говорить просто, про проверку изначальну. что число long double не NAN или не infifnity, вспомнил только я.
про то, что не надо скопом всё сразу на червонец умножать, пока число не приведёт к переполнению, а отхватывать после каждого умножения на 10, опять же вспомнил только я. (я в своём алгоритме даже не рассматриваю пределы максимальных значений int). кроме простого вывода в строку (опят же написал я), есть ещё и варианты printf свыводом не на устройство вывода, а опять же в строку. Если Кнут сделал алгоритм, то зашибись ( хотя какие мне дела до Кнута, я алгоритмист не хуже) -------------------- a = a + b; b = a - b; a = a - b; |
|||
|
||||
![]() ![]() ![]() |
Правила форума "C/C++: Для новичков" | |
|
Запрещается! 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делиться вскрытыми компонентами
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: Для новичков | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |