needDrivers wrote:
> Понадобилось отделить дробную часть от числа (копейки отделить от рублей). > Подскажите, что я делаю не так?
Ты неправильно используешь тип DOUBLE. В переменных такого типа
воообще нет недробной части. Всё число -- дробная часть.
Так что что от чего ты собираешся отделять -- вообще непонятно.
Здравствуйте, needDrivers, Вы писали:
D>Хорошо, как сделать правильно? D>Есть число 3.03 из него надо получить 3 рубля 3 копейки.
К сожалению, нет числа 3.03. Оно непредставимо в двоичной плавающей форме, там в действительности что-то вида 3.029999999... (с ошибкой в 14 знаке).
Выходов три:
— Пользоваться double для рублей, но постоянно принуждать к округлению до 3 знака после запятой
— Пользоваться int для копеек
— Написать собственный тип currency, реализующий десятичную, а не двоичную арифметику. Вот не помню, поддерживает ли интеловский FPU десятичную плавающую точку по IEEE, или нет. Если да, то можно будет привлечь ассемблер. Если нет — то всё ручками.
Здравствуйте, needDrivers, Вы писали:
D>Хорошо, как сделать правильно? D>Есть число 3.03 из него надо получить 3 рубля 3 копейки.
Надо понять для начала, что нет такого числа. Это десятичное представление двоичного числа, т.е. аппроксимация двоичного значения десятичным. Т.е. если число 3.03 (сумма) и точно представимо, это не значит, что числа 3.0 и 0.03 точно представимы.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Мне нужно преобразовать double в "число прописью".
Нашёл функцию, которая преобразовывает целое в пропись.
Я решил разбить double на целую и дробную часть, но в копейках появляется неточность.
3 копейки превращаются в 2
5 копеек в 4 и т.п.
Наверняка кто-нибудь делал подобное. Как правильно реализовать данную функцию?
Здравствуйте, needDrivers, Вы писали:
D>Мне нужно преобразовать double в "число прописью". D>Нашёл функцию, которая преобразовывает целое в пропись. D>Я решил разбить double на целую и дробную часть, но в копейках появляется неточность. D>3 копейки превращаются в 2 D>5 копеек в 4 и т.п. D>Наверняка кто-нибудь делал подобное. Как правильно реализовать данную функцию?
Тебе не число надо реализовывать, а изначально считать в десятичной системе, если хочешь десятичную точность. Посмотри библиотеку GMP
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
V>Тебе не число надо реализовывать, а изначально считать в десятичной системе, если хочешь десятичную точность. Посмотри библиотеку GMP
Как понять: считать в десятичной системе? Хранить отдельно целую и отдельно дробную части числа?
Я ничего не рассчитываю. Есть БД в которой хранятся суммы в формате double. Надо в печатную форму выводить эти суммы в виде чисел и прописью.
12000.03 (Двенадцать тысяч рублей 3 копейки)
Здравствуйте, needDrivers, Вы писали:
D>Как понять: считать в десятичной системе? Хранить отдельно целую и отдельно дробную части числа? D>Я ничего не рассчитываю. Есть БД в которой хранятся суммы в формате double. Надо в печатную форму выводить эти суммы в виде чисел и прописью. D>12000.03 (Двенадцать тысяч рублей 3 копейки)
Тогда, типа, так:
// s - сумма (double)
// r - рубли (целое)
// k - копейки (целое)
// 0.005 - полкопейки на всяко-разно вокруг doubleconst int r = (s + 0.005);
const int k = (s - r + 0.005) * 100;
Здравствуйте, needDrivers, Вы писали:
D>Здравствуйте!
D>Понадобилось отделить дробную часть от числа (копейки отделить от рублей). D>Подскажите, что я делаю не так?
Немного не по теме, а разве для денег используют числа с плавающей точкой?
Кое-где, в частности в Delphi, есть тип currency с фиксированной точкой.
В С, я думал, следует использовать long long.
Кодт wrote:
> Выходов три: > — Пользоваться double для рублей, но постоянно принуждать к округлению > до 3 знака после запятой > — *Пользоваться int для копеек* > — Написать собственный тип currency, реализующий десятичную, а не > двоичную арифметику.
Есть третий путь. Использовать один из готовых таких классов.
Или библиотек.
Вот не помню, поддерживает ли интеловский FPU > десятичную плавающую точку по IEEE, или нет. Если да, то можно будет > привлечь ассемблер. Если нет — то всё ручками.
Там есть поддержка BCD, не в FPU, а в основном процессоре. Хотя
на сколько я понимаю уже давно всё на одном кристалле.
needDrivers wrote:
> Я ничего не рассчитываю. Есть БД в которой хранятся суммы в формате > double.
Это очень плохо. Значит, вам придётся переделывать и БД тоже
Проверь, может быть в БД всё же какой-то NUMERIC, а ты только
на клиенте его в виде double получаешь.
Alexander G wrote:
> Немного не по теме, а разве для денег используют числа с плавающей точкой?
Ну как, если вменяемые разработчкики, и если это не
статистические расчёты , то не используется.
(для статистики как раз лучше (ну, возможно) плавующуу точку использовать,
потому как средняя зарплата в 20000 рублей 25.6 копеек --
это нормально. А вот выданная зарплата за февраль Иванову 20000 рублей 25.6
копеек -- это нонсенс.)
на самом деле вам тут насоветовавали кучу всякой туфты, double отлично может хранить цену, многие торговые терминалы так и делают,
что касается вашего кода, то 2 раза вызывать modf — смысла нет, одного вполне достаточно
D>
D> char tmp[1024];
D> double zz;
D> double yy;
D> int i;
yy = modf(3.03, &zz);
i = int(100 * (yy + DBL_EPSILON)) // ну или std::numeric_limits<double>::epsilon() вместо DBL_EPSILON если от си тошнит
D>
В БД действительно используется double, потому что таблицу создавал я.
Double использовал от недостатка знаний и о типе данных с фиксированной точкой только догадывался.
Спасибо большое за советы, в дальнейшем буду переходить на фиксированную точку.
Не могли бы дать хорошие примеры или советы по работе с фиксированной точкой?
Какой тип данных использовать для хранения чисел с фиксированной точкой __int64?
needDrivers wrote:
> Не могли бы дать хорошие примеры или советы по работе с фиксированной > точкой?
Ну, какие тут могут быть советы ...
Используй -- вот главный совет.
Отличай случаи, когда надо использовать, от случаев, когда не надо
(редко, и в основном -- статистические расчёты). Ну т.е. надо прежде
всего решить, какой тип арифметики использовать: точную или плавающую.
Если точная арифметика -- надо чётко понимать, как при вычислении
выражений округлять результаты, и в каком месте. Постановщики задач
часто об этом не думают, надо из них это вытрясать.
Если не будешь округлять, можно получить переполнение достаточно быстро.
Правило вычисления точности результата:
N1.M1 * N2.M2 = N1+N2.M1+M2 (если не ошибаюсь),
где
N1, N2 -- кол-во цифр до дес. точки,
M1, M2 -- кол-во цифр после дес. точки.
> Какой тип данных использовать для хранения чисел с фиксированной точкой
На клиенте СУБД нужно использовать либо типы данных, предоставляемые
клиентской библиотекой СУБД, если такие есть, либо какие-то универсальные
типы данных, предоставляемые языком, или библиотекой, или ещё чем-то
(как напр. currency в VB). Ну, или использовать библиотеки длинных
точных чисел, типа Gnu MP.
Здравствуйте, needDrivers, Вы писали:
D>Здравствуйте!
D>Понадобилось отделить дробную часть от числа (копейки отделить от рублей). D>Подскажите, что я делаю не так?
Никогда, никогда, никогда нельзя использовать типы с плавающей точкой для хранения величин за точность которых можно получить по еб@лу.