Здравствуйте, -Cheese-, Вы писали:
C>объясни попробуй клиенту про двоичное представление числа с плавающей точкой много нового о себе услышишь C>ну да ладно... будем вводить эпсилон промежуток
А может быть, сделать арифметику с фиксированной точкой? Грубо говоря, считать всё в int64 в копейках.
#include <cmath>
int main ()
{
const int p = 1000;
long long x = 800000;
long long y = 5065;
double z = std::ceil ((x * y) / static_cast
<double> (p * p));
double q = (x * y) / static_cast
<double> (p * p);
return 0;
}
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
"В любое мгновение принятия решения, лучшее, что вы можете сделать, это принять правильное решение; следующим лучшим вариантом будет принять неправильное решение, худший вариант – не принимать решения совсем" (c) Теодор Рузвельт.
Здравствуйте, -Cheese-, Вы писали:
C>Я понимаю, что типа хранение чисел с плавающей точкой и всё такое... но как избежать этого?
Например использовать целочисленую арифметику до самого последнего момента;
C>Получаем: C>z1=4053.0000000 C>z2=4052.0000000
А что должны получить?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
"В любое мгновение принятия решения, лучшее, что вы можете сделать, это принять правильное решение; следующим лучшим вариантом будет принять неправильное решение, худший вариант – не принимать решения совсем" (c) Теодор Рузвельт.
Здравствуйте, np9mi7, Вы писали:
N>Здравствуйте, -Cheese-, Вы писали:
C>>Я понимаю, что типа хранение чисел с плавающей точкой и всё такое... но как избежать этого?
N>Например использовать целочисленую арифметику до самого последнего момента;
C>>Получаем: C>>z1=4053.0000000 C>>z2=4052.0000000
N>А что должны получить?
4052.0000000
Объясняю смысл проблемы...
есть некоторые платежи для человека в некоторой валюте ,
например
1) 5.09
2) 31.091
3) 1.01
......
и есть итоговая сумма — например: 37.191
но с человека нужно брать в этом случае 37.20 = ceil(sum(x_i)), x_i — i-тый платёж
на может сложиться ситуация когда человек должен заплатить например ровно 12 у.е.,
а ceil(sum(x_i)) может равняться 12.01 у.е., что не есть хорошо...
Здравствуйте, np9mi7, Вы писали:
N>Например так:
N>
N>#include <cmath>
N>int main ()
N>{
N> const int p = 1000;
N> long long x = 800000;
N> long long y = 5065;
N> double z = std::ceil ((x * y) / static_cast
N> <double> (p * p));
N> double q = (x * y) / static_cast
N> <double> (p * p);
N> return 0;
N>}
N>
Хм... Думаю, должен быть велосипедный парк на тему вычислений с фиксированной точкой... В крайнем случае, можно (и нужно) написать самому.
Собственно, необходимых операторов-то: +, -, *, /, double().
Здравствуйте, -Cheese-, Вы писали:
N>>А что должны получить? C>4052.0000000
А напрасно! ceil — это округление вверх до целого! floor, соответственно — вниз.
C>Объясняю смысл проблемы... C>есть некоторые платежи для человека в некоторой валюте , C>например C>1) 5.09 C>2) 31.091 C>3) 1.01 C>......
C>и есть итоговая сумма — например: 37.191 C>но с человека нужно брать в этом случае 37.20 = ceil(sum(x_i)), x_i — i-тый платёж
Здесь нужно округление вверх до 0.01 — это ceil(the_sum/0.01)*0.01
C>на может сложиться ситуация когда человек должен заплатить например ровно 12 у.е., C>а ceil(sum(x_i)) может равняться 12.01 у.е., что не есть хорошо...
Видимо, нужно округление к ближайшему 0.01 — это floor(the_sum/0.01 + 0.5)*0.01
Но вообще говоря, плавающая арифметика — не лучшее решение для представления денег.
Особенно — арифметика с двоичной дробной частью (поскольку десятичные дроби оказываются непредставимы и хранятся в приближённом виде — там стремительно набегают ошибки округления). К этому добавляются ошибки нормализации, присущие плавающей арифметике вообще, не только двоичной.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, -Cheese-, Вы писали:
N>>>А что должны получить? C>>4052.0000000
К>А напрасно! ceil — это округление вверх до целого! floor, соответственно — вниз.
дело в том, что если в столбик умножить , то получится 4052
Здравствуйте, -Cheese-, Вы писали:
C>дело в том, что если в столбик умножить , то получится 4052
В какой системе умножаешь в столбик ты, и в какой — компьютер.
5.065 непредставимо в виде double — там, на самом деле, что-то наподобие 5.0650000000001. При умножении эта ошибка вылезает.
Здравствуйте, Кодт, Вы писали: К>В какой системе умножаешь в столбик ты, и в какой — компьютер. К>5.065 непредставимо в виде double — там, на самом деле, что-то наподобие 5.0650000000001. При умножении эта ошибка вылезает.
в том то и проблема...
объясни попробуй клиенту про двоичное представление числа с плавающей точкой много нового о себе услышишь
ну да ладно... будем вводить эпсилон промежуток
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[2]: Округление с помощью ceil...
От:
Аноним
Дата:
17.10.06 15:38
Оценка:
Здравствуйте, Axc, Вы писали:
Axc>Здравствуйте, -Cheese-
Axc>Я бы делал так: Axc>
Axc> const double accuracy = 1E-6;
Axc> double x = 800.0;
Axc> double y = 5.065;
Axc> double z1 = ceil(x * y * (1 - accuracy));
Axc> double z2 = x * y;
Axc> double z3 = floor(x * y * (1 + accuracy));
Axc>
Ну будет ли так любезен уважаемый -Cheese- ответить пару "почему"?
А именно, интерисует:
1) почему accuracy = 1E-6
2) почему в одном случае 1-accuracy, а в другом 1+accuracy.
Александр
Re[3]: Округление с помощью ceil...
От:
Аноним
Дата:
17.10.06 21:10
Оценка:
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Axc, Вы писали:
Axc>>Здравствуйте, -Cheese-
...
А>Ну будет ли так любезен уважаемый -Cheese- ответить пару "почему"?
Черт... Ошибочка вышла. Следует читать:
Ну будет ли так любезен уважаемый axc ответить пару "почему"?
Здравствуйте, -Cheese-, Вы писали:
C>Объясняю смысл проблемы... C>есть некоторые платежи для человека в некоторой валюте , C>например C>1) 5.09 C>2) 31.091 C>3) 1.01 C>......
C>и есть итоговая сумма — например: 37.191 C>но с человека нужно брать в этом случае 37.20 = ceil(sum(x_i)), x_i — i-тый платёж
C>на может сложиться ситуация когда человек должен заплатить например ровно 12 у.е., C>а ceil(sum(x_i)) может равняться 12.01 у.е., что не есть хорошо...
Ну-ну. Один знакомый программист рассказывал, как они 2 ночи подряд искали почему годовой баланс банка не сходится на 4 копейки, где же они неправильно округлили число с плавающей запятой
Здравствуйте, Кодт, Вы писали: К>Здравствуйте, -Cheese-, Вы писали: C>>объясни попробуй клиенту про двоичное представление числа с плавающей точкой много нового о себе услышишь C>>ну да ладно... будем вводить эпсилон промежуток К>А может быть, сделать арифметику с фиксированной точкой? Грубо говоря, считать всё в int64 в копейках.
Вообще-то все расчёты и ведутся в копейках, но увы может быть такое, что нужно нецелое число копеек (например обмен валюты по курсу 5.065234152).
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: Округление с помощью ceil...
От:
Аноним
Дата:
18.10.06 05:57
Оценка:
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, np9mi7, Вы писали:
N>>Здравствуйте, -Cheese-, Вы писали:
C>>>Я понимаю, что типа хранение чисел с плавающей точкой и всё такое... но как избежать этого?
N>>Например использовать целочисленую арифметику до самого последнего момента;
C>>>Получаем: C>>>z1=4053.0000000 C>>>z2=4052.0000000
N>>А что должны получить? C>4052.0000000
C>Объясняю смысл проблемы... C>есть некоторые платежи для человека в некоторой валюте , C>например C>1) 5.09 C>2) 31.091 C>3) 1.01 C>......
C>и есть итоговая сумма — например: 37.191 C>но с человека нужно брать в этом случае 37.20 = ceil(sum(x_i)), x_i — i-тый платёж
C>на может сложиться ситуация когда человек должен заплатить например ровно 12 у.е., C>а ceil(sum(x_i)) может равняться 12.01 у.е., что не есть хорошо...
Где только вас учили метематике!!
Простейшее округление делается так:
E — поправочный коэффициент = 0.5
M — множитель равный 10 в степени N(где N количество знаков после запятой)
S — число которое нужно округлить.
R — результат.
R = (floor(S * M + E)) * M, либо
R = (ceil(S * M — E)) * M
Здравствуйте, <Аноним>, Вы писали:
А>Где только вас учили метематике!! А>Простейшее округление делается так: А>E — поправочный коэффициент = 0.5 А>M — множитель равный 10 в степени N(где N количество знаков после запятой) А>S — число которое нужно округлить.
А>R — результат. А>R = (floor(S * M + E)) * M, либо А>R = (ceil(S * M — E)) * M
Где меня только не учили математике... последний раз меня подучили на работе.
А ещё есть ТЗ, где ясно сказано, что даже при E=0.000000000000000000000000000000000000000001
результат округления x+E будет x+1, где х принадлежит множеству целых неотрицательных чисел (в данном случае)...
вот
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[5]: Округление с помощью ceil...
От:
Аноним
Дата:
18.10.06 06:35
Оценка:
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, <Аноним>, Вы писали:
А>>Где только вас учили метематике!! А>>Простейшее округление делается так: А>>E — поправочный коэффициент = 0.5 А>>M — множитель равный 10 в степени N(где N количество знаков после запятой) А>>S — число которое нужно округлить.
А>>R — результат. А>>R = (floor(S * M + E)) * M, либо А>>R = (ceil(S * M — E)) * M
C>Где меня только не учили математике... последний раз меня подучили на работе. C>А ещё есть ТЗ, где ясно сказано, что даже при E=0.000000000000000000000000000000000000000001 C>результат округления x+E будет x+1, где х принадлежит множеству целых неотрицательных чисел (в данном случае)... C>вот
Опечатался. Вот так будет правильно.
R = (floor(S * M + E)) / M, либо
R = (ceil(S * M — E)) / M
Здравствуйте, -Cheese-, Вы писали:
C>Вообще-то все расчёты и ведутся в копейках, но увы может быть такое, что нужно нецелое число копеек (например обмен валюты по курсу 5.065234152).
Представляю себе надпись на Сбер. Банке "Курс валют: 1 $ = 26.9445065234152 руб.";
Может можно держать курс целым числом 1 Цент = Целое Коэффициент * 1 Копейку? Или если всё это дело обобщить, то необходимо научиться переводить Условные Единицы одной валюты (Копейка, десятая часть копейки, сотая часть копейки...) в Условные Единицы другой вылюты (Цент, десятая часть цента, сотая часть цента...) с помощью Целого Коэффициента;
В итоге сумму счета получать по формуле:
Сумма = Количество У.Е. * 10 ^ Экспонента Валюты;
Экспонента Валюты это -2 для копеек, -3 для десятой части копейки, -4 для сотой части копейки...
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
"В любое мгновение принятия решения, лучшее, что вы можете сделать, это принять правильное решение; следующим лучшим вариантом будет принять неправильное решение, худший вариант – не принимать решения совсем" (c) Теодор Рузвельт.
Здравствуйте, <Аноним>, Вы писали:
А>Тогда со спокойной совестью приводите к типу int и смело прибавляйте 1, так как тип double всегда что-то там имеет в хвосте.
Тут... Re[2]: Округление с помощью ceil...
Здравствуйте, -Cheese-, Вы писали:
C>Вообще-то все расчёты и ведутся в копейках, но увы может быть такое, что нужно нецелое число копеек (например обмен валюты по курсу 5.065234152).
Ну что же, значит вот в этом месте и нужно умножать фиксированное на плавающее и получать фиксированное, причём по тому закону, который требуется — а не по тому, который забит в FPU.
Возможно, придётся поддержать несколько фиксированных форматов — *10^-2 (валюты) и *10^-9 (коэффициенты).
Либо плавающую арифметику с целой мантиссой и десятичным порядком.
Здравствуйте, -Cheese-, Вы писали:
C>Здравствуйте, Кодт, Вы писали: К>>Здравствуйте, -Cheese-, Вы писали: C>>>объясни попробуй клиенту про двоичное представление числа с плавающей точкой много нового о себе услышишь C>>>ну да ладно... будем вводить эпсилон промежуток К>>А может быть, сделать арифметику с фиксированной точкой? Грубо говоря, считать всё в int64 в копейках.
C>Вообще-то все расчёты и ведутся в копейках, но увы может быть такое, что нужно нецелое число копеек (например обмен валюты по курсу 5.065234152).
А числа плавающей запятой всё равно ничего не решат, только усугубят, т.к. точность всё равно конечная. Т.е. любой курс конвертации и сумму точно всё равно не представишь. Всё равно не каждое число можно ровно поделить на 2 части. Всё равно если с числом попроизводить какие-то вычисления и ожидать получить ровно 2,9, можешь получить 3,1.
Здравствуйте, <Аноним>, Вы писали:
Axc>>Я бы делал так: Axc>>
Axc>> const double accuracy = 1E-6;
Axc>> double x = 800.0;
Axc>> double y = 5.065;
Axc>> double z1 = ceil(x * y * (1 - accuracy));
Axc>> double z2 = x * y;
Axc>> double z3 = floor(x * y * (1 + accuracy));
Axc>>
А>1) почему accuracy = 1E-6
Ни почему. Постановка задачи на тот момент еще не была уточнена. Соответственно, нужная точность была взята с потолка и задана константой. Вообще же, данный способ годится для научных расчетов, но не для денег. А>2) почему в одном случае 1-accuracy, а в другом 1+accuracy.
Потому что ceil надо брать от числа чуть меньшего, а floor от чуть большего.