Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > C/C++: Для новичков > Преобразование вещественной части в целое |
Автор: den140387 20.8.2013, 11:01 | ||
Добрый День! Прошу помочь в написании кода преобразовании вещественной части 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 20.8.2013, 11:10 | ||
Ну вы же читали про вещественные типы ![]()
|
Автор: den140387 20.8.2013, 11:17 | ||||
Спасибо! ![]() Я читал про них в Лафоре и Липпмане. Там говорится только то, что long double имеет точность в 19 цифр, если я правильно помню. В связи с этим вопрос: почему вы выбрали именно "0.001", а не "0.01" или же "0.00001"? Раз точность 19 цифр, то по идее 18-ть нулей должно быть, если я ничего не путаю.. |
Автор: den140387 20.8.2013, 11:35 | ||||||||||||||
Как я понимаю, они просто по-другому в памяти машины представляются. Для плавающих есть мантисса и экспонента, а для целочисленных - нет
Ну как я понимаю, дробная часть с точностью до 19ти цифр. А почему не имеет решения? Мне же известна максимальная точность long double: 19 цифр после запятой.
По поводу строки я уже сделал. Было две задачи: 1) Пользователь вводит строку. Необходимо вывести это же значение в формате long double Это я сделал. 2) Пользователь вводит число типа long double. Оно должно быть прочитано как long double. Необходимо вывести его в виде строки. Целую часть в строку я преобразовал. А вот с дробной возникла проблема. |
Автор: SenkraD 20.8.2013, 11:59 | ||||||
|
Автор: feodorv 20.8.2013, 12:13 | ||||
Простите, но это совсем другая задача. Не нужно её сводить к нахождению дробной части в виде беззнакового целого. Да и целая часть 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 20.8.2013, 14:10 | ||
Первоначально у нас вводится строковый тип. Так и работайте с ним. Предположим, что точка в вашей текущей локали разделитель целой и дробной части
Это в предположении, что ввод в десятичной системе, но без "E-2", например. |
Автор: akizelokro 20.8.2013, 14:25 | ||
Вообще-то, он в любом случае вводит строку, если считать для упрощения. Есть в стандарте функции to_tstring (на самом деле to_string и to_wstring, но мне сподручней писать сразу to_tstring) И разные функции обратного преобразования из строки в числовые значения stol, stoi, stod .. Кажисть, правда в С++11 стандарте. но эти функции уже и студия даже поддерживает |
Автор: akizelokro 20.8.2013, 14:43 |
а если проблема с преобразованием вещественной части и нельзя прибегать к стандартным этим функция преобразования, то берёте вещественную часть и по циклу умножаете каждый раз на 10, новая целая часть даёт очередную цифру вещественной части. Её пишете и целую часть отбрасываете, и снова повтор умножения на 10. И там до тех пор пока результат не станет равен нулю. |
Автор: feodorv 20.8.2013, 17:43 | ||
Всё так. Но: Результат может никогда не стать равным нулю... |
Автор: den140387 20.8.2013, 19:21 | ||||||||||||
Спасибо! ![]() Добавлено через 5 минут и 5 секунд
c int - все просто. Я могу легко пройтись по всему целому числу и каждую цифру преобразовать в символ, путем сложения с '0'. В случае обхода целочисленного значения, я могу легко получить условие того, что обработал все цифры этого значения. А вот с вещественными типами проблема из-за того, что я не знаю, когда заканчивается это число. Добавлено через 7 минут и 26 секунд
+1. В способе, который я привел в самом начале, происходило зацикливание. |
Автор: akizelokro 20.8.2013, 19:30 |
Неочевидно. Десять в минус какой степени там предел? tstring схавает. А если уж кому-то интересно, то можно break на пятитысячной итерации поставить. |
Автор: den140387 20.8.2013, 19:32 | ||
Спасибо! ![]() Вы гораздо более понятно объяснили, чем в книжке. |
Автор: akizelokro 20.8.2013, 20:26 | ||||||
Это где вы делали сравнение double с интом? Ну-ну! Там вообще с вашим умножением на 10 в цикле может быть неизвестно что. Об этом вам писали уже. Вам надо было сравнивать именно с нулём (значение double), постоянно отсекая целую часть (появившуюся цифру), перенося её в строку. и там уже не нужны никакие ограничения на размер инта. Работа ведётся только с double и string. А привлекать ещё дополнительный тип, когда можно обойтись и без него, нерационально. Единственное, что его можно использовать для того, чтобы цифру, появлюящуюся после умножения на 10 вещественной части преобразовывать в литерал (строку). Есть разница между
и
А вот это именно и есть разница в подходах. Бритва Оккама. О чем и был мой первый пример кода. string может захавать сколько угодно цифр после запятой. А проверку на NaN'ы и прочие эники-беники тоже реализуете Добавлено через 29 секунд И не надо ничего делить на десять. Добавлено через 9 минут и 39 секунд Просто double c int сравнивать никогда не надо. Так, совет на будущее. Потому что результат вообще неочевиден |
Автор: feodorv 21.8.2013, 03:21 | ||||||||
Есть! Только я не понимаю, как умножая на 10 Вы собираетесь свести ldval к нулю:
По идее, с нулём нужно сравнивать дробную часть ldval:
На выходе получается что-то вроде
Ну, благодаря тому, что в числе 10 есть множитель двойка (а про двоичное машинное представление я уже говорил), предел, конечно, наступит. Каждое очередное умножение на двойку обнуляет очередной бит в хвосте мантиссы. Поскольку таких битов 63, то получаем максимальную итерацию - 63. Но если бы Вы захотели распечатать число, скажем, в 5-ричном виде, то конца бы не дождались))) На 10 происходит неявное деление, как только Вы пишите:
И вот тут машинное двоичное 0.456 не совпадает с нашим десятиричным 0.456 - чуть чуть, но отличается. PS Сравнение чисел с плавающей точкой с нулём, вообще говоря, не корректно. |
Автор: akizelokro 21.8.2013, 07:09 | ||||||||||||
комментарии пишутся для того, чтобы их читали
и почти это как раз вы сделали. (почему вы это сделали "почти", обяснение чуть ниже. не надо было изначально умножать в цикле на 10 до упора, не обнуляя целую часть, и всё это пытаться поместить в int'овскую переменную. и, кстати, из приведенного кода неочевидно, что
из-за куска в коде
здесь опять же идёт интересный момент double -= (double)(int)double поставьте число на вывод сразу после определения
конечно бы не дождался. такого варианта задачи я себе даже не ставил. точный же метод решения задачи был мной сразу приведён. сразу "хавать" введённое число с клавиатуры в string и парсить его. Добавлено @ 07:23 в принципе, можно при реализации алгоритм и подправить, учесть знак, умножить на "-1" например, если вообще интересует тогда уж строгое соответствие делает не сравнение, что не равно 0, а сравнение больше-равно нулю, отнимать не (int)х, а double-единицу в цикле пока число не станет меньше 1, чтобы не путаться лишний раз с приведением double к int и обратно. Мораль сего спича такова, вообщем. Если есть задача вывести целую или вещественную часть double-числа в строковый вид, то не надо налегать на int'ы. Это уже лишняя сущность и использовать её нужно желательно по минимуму, потому что в логике задачи не требуется. |
Автор: feodorv 21.8.2013, 08:12 | ||||||||
Моё (сугубо индивидуалистическое) видение решения задачи выглядит так:
Результаты получаются такого рода:
Что не хорошо (помимо артефактов деления на 10).
Что-то мне подсказывает, что >> не умеет читать long double, а только double... Да и вообще, long double лишь синоним double (это у Microsoft). |
Автор: feodorv 21.8.2013, 08:39 | ||
Прошу прощения, пропустил, увлёкся кодом))) Остальное ничего не понял(((
Если не нравится (double)(int)double (чем именно?) пользуйтесь modf. Что плохого в (double)(int)double? Ну и что, что лишнее приведение, главное, в точности потерь нет. Какие тонкости нужно выдерживать? Как именно их выдерживать - вычитать по 1.0??? И что это даст? Вы в курсе, что при этом будет задействован алгоритм, описанный ещё у Кнута, по "сглаживанию" вывода. То есть вывод != машинному представлению числа. |
Автор: akizelokro 21.8.2013, 10:32 | ||||
Я просто не уверен, что в случае (double)(int)3.123 получится именно 3.0, а не опять очередная потеря точности Поэтому и не хочу применять преобразования double к int и обратно. Это к утверждению, почему возможно, что не аргумент в данном случае.
Да ничего там не будет такого. А будет там просто считывание ввода сразу в строку - tcin >> <строка>. Предположу, что как раз это и нужно автору темы (раз тема для новичков), а не подробные уже дебри. |
Автор: feodorv 21.8.2013, 11:07 | ||||||
Может, я чего не понимаю, но невидно никаких подводных камней по этому поводу. Число x - хорошее, не inf, не nan, < 10 и >= 1, откуда потеря точности?
![]() ![]()
|
Автор: akizelokro 24.8.2013, 13:55 |
Давай говорить просто, про проверку изначальну. что число long double не NAN или не infifnity, вспомнил только я. про то, что не надо скопом всё сразу на червонец умножать, пока число не приведёт к переполнению, а отхватывать после каждого умножения на 10, опять же вспомнил только я. (я в своём алгоритме даже не рассматриваю пределы максимальных значений int). кроме простого вывода в строку (опят же написал я), есть ещё и варианты printf свыводом не на устройство вывода, а опять же в строку. Если Кнут сделал алгоритм, то зашибись ( хотя какие мне дела до Кнута, я алгоритмист не хуже) |