Модераторы: LSD, AntonSaburov
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Округление double, для денежных вычислений например... 
:(
    Опции темы
firedrago
Дата 26.9.2005, 01:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


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

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



Всем привет,
посоветуйте как точнее всего реализовать округление double до сотых или тысячных....

я делал это так, но работает оно как-то не точно.....хотя по идее должно....

при раскладе Nbr.formatDouble(1.0049, 2) получается 1.00 а мне надо 1.01

Код

    public static String formatDouble(double number,int fractionDigits) {
        NumberFormat nf = NumberFormat.getNumberInstance(Locale.GERMANY );
        String res = "";
        nf.setMinimumFractionDigits(fractionDigits);
        nf.setMaximumFractionDigits(fractionDigits);
        res = nf.format(number);
            
        return res;
    }



PM MAIL   Вверх
Ch0bits
Дата 26.9.2005, 02:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Python Dev.
****


Профиль
Группа: Завсегдатай
Сообщений: 2124
Регистрация: 21.2.2005
Где: Казань

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



Потому что [0.000; 0.005] = 0.00 и (0.005; 0.01] = 0.01
1.0049 так и получается 1.00, по моему так и должно быть. smile

Это сообщение отредактировал(а) Vadim999 - 26.9.2005, 02:11
PM WWW   Вверх
Macross
Дата 26.9.2005, 08:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Если мне память не изменяет, то по правилам математики 1.0049 округляеться именно до 1.00. Округление идет по следующему знаку за нужным, в данном случае по третьему, а 4 округляеться до 0.
PM MAIL   Вверх
firedrago
Дата 26.9.2005, 11:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


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

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



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


Эксперт
***


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

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



firedrago, тогда отсылай в процедуру не number, а
Код

java.lang.Double.Trunc(number * 1000) / 1000



--------------------
Может быть, это только мой бред,
Может быть, жизнь не так хороша,
Может быть, я не выйду на свет,
Но я летал, когда пела душа...
PM MAIL   Вверх
Ch0bits
Дата 26.9.2005, 12:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Python Dev.
****


Профиль
Группа: Завсегдатай
Сообщений: 2124
Регистрация: 21.2.2005
Где: Казань

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



Цитата(firedrago @ 26.9.2005, 11:02)
но если для бугалтерии, то спустя какое-то время будет потеря денег

Поэтому там считают с большей точностью и только в отчётах округляют до копеек.
PM WWW   Вверх
Guest
Дата 26.9.2005, 13:53 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











Могу посоветовать следующее, как сделать проще не знаю smile

Код

public static String formatDouble(double number,int fractionDigits) {        
        Pattern p=Pattern.compile("\\d*\\.\\d{"+fractionDigits+"}(\\d*)");
        Matcher matcher=p.matcher(Double.toString(number));
        matcher.find();
        double t=Double.parseDouble(matcher.group(1));
        int ret=0;
        while(t>1){            
            t/=10;            
            t=Math.round(t);            
        }
        for(int i=0;i<fractionDigits;i++)t/=10;        
        
        NumberFormat nf = NumberFormat.getNumberInstance(Locale.GERMANY );    
        String res = "";    
        nf.setMinimumFractionDigits(fractionDigits);    
        nf.setMaximumFractionDigits(fractionDigits);    
        res = nf.format(number+t);    
             
        return res;    
}

  Вверх
firedrago
Дата 26.9.2005, 14:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


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

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



всем спасибо, я думаю для начала подойдет.
если еще какие-то идеи возникнут, буду благодарен.

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


Бывалый
*


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

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



firedrago
Цитата

да округление математически правильное, но если для бугалтерии, то спустя какое-то время будет потеря денег...


Не знаю, полезно ли, но рекомендую почитать статьи об округлении в финансовых расчетах.
Там куча правил, очень мало, что имеющих с обычным округлением.

Вот, более-менее стандартные типы (признанные математиками), разумеется, это только то немногое, что я вспомнил из того, чего не знаю smile

1.35 = 1.4
1.35 = 1.3
1.39 = 1.3 (не округление, а просто "отбрасывание" "лишних" цифр)
1.35 = 1.4 но 1.45 = 1.4 (округление в нечетную сторону)
Встречал и варинат
1.351= 1.3, но 1.356 = 1.4

Тут все дело в том, что в финансах в обычную математику вмешивается целая куча параметров, таких как, например, необходимость использования бесконечных дробей (типа партия из 3 товаров за 10 рублей в принципе не может быть реализована строго по 10/3 руб. за штуку).
Необходимость расчета с максимальной точностью вплоть до заданного отчетного периода, после которого сумма округляется и далее цикл повторяется, потом идет перерасчет с налоговой на сальдо и сумма снова корректируется.
Целые статьи пишутся о том, как максимально сгладить эффект сальдо при финансовых расчетах.

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

Например, для простейшего варианта округления (внимание! работает неверно для большого кол-ва знаков после запятой, >7, кажется, что можно решить введением BigDecimal)
можно написать что-то вроде (разумеется, неоптимизированный вариант, я его только сейчас изобрел smile

Код


public class MyMath {

  public static void main(String[] args) {
    System.out.println(round_BusinessRule1_234(12.0049,2,1));
  }
  
  //shift - это сдвиг, в вашем примере - 1.
  public static   double  round_BusinessRule1_234( double myNum, int digits, int shift)
  {
    double   tmp = Math.pow(10.0, digits+shift);
    double result= java.lang.Math.round(myNum*tmp)/tmp;
    tmp = Math.pow(10.0, digits);
    return java.lang.Math.round(result*tmp)/tmp;
  }

}






PM MAIL   Вверх
Guest
Дата 4.10.2005, 19:52 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











В азбуке Refactoring: Improving the Design of Existing Code by Martin Fowler,
Фаулер пишет:
Цитата
These examples are there only to illustrate the refactorings, nothing more. In particular you'll notice that in the examples I use double to represent monetary values. I've done this only to make the examples simpler, as the representation is not important to the refactoring. I strongly advise against using doubles for money in commercial software. When I represent money I use the Quantity pattern [Fowler, AP].

[Fowler, AP] это видимо отсыл к M.Fowler Analysis Patterns: Reusable Object Models. Reading, Mass.: Addison-Wesley, 1997. A book of domain model patterns.
http://www.google.ru/search?q=quantity%20pattern
  Вверх
firedrago
Дата 4.10.2005, 23:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


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

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



я тут еще нашел интересную библиотеку на яве Java Financial Numerics Toolkit с исходниками, там много всего есть, правда еще времени не было с ней разобраться....
и в отношении округленяй вот что в ней...
Код

package com.financelabs.numerics;

import java.math.BigDecimal;

/**
 * This class provides a set of general purpose/shared utility methods that are 
 * used by classes in the numerics package. 
 */
public class Utilities 
{
    /**
     * This method rounds the supplied decimal to the specified number of
     * decimal places.
     * @param a_dDecimal The decimal to round
     * @param a_iScale The number of places to scale the decimal to
     * @return The decimal (a_dDecimal) scaled to the specified number of 
     *          places i.e. (a_iScale). 
     */
    public static double round(double a_dDecimal, int a_iScale)
    {
        double result;

        if (a_iScale>0)
        {
            boolean l_bNegate=(a_dDecimal<0);

            BigDecimal l_bgDecimal=new BigDecimal(a_dDecimal);
            BigDecimal l_bgScaledDecimal=l_bgDecimal.setScale(a_iScale,BigDecimal.ROUND_DOWN);
            BigDecimal l_bgResult = l_bgScaledDecimal.abs();

            if (l_bNegate) //if it was less than zero anywhere, then negate it again
                result = l_bgResult.negate().doubleValue();
            else
                result = l_bgResult.doubleValue();
        }
        else
            result=a_dDecimal;
        
        return result;
    }


    /**
     * This method calculates the payoff of an option
     * @param a_dStrikePrice The strike price of the option
     * @param a_dUnderlyingAssetPrice The price of the underlying asset
     * @param a_bCall Specifies whether the option is a call or put
     * @param a_iScale Specifies the number of places to scale the result to
     * @return The payoff of the option
     */
    public static double calculatePayoff(   double a_dStrikePrice,
                                            double a_dUnderlyingAssetPrice,
                                            boolean a_bCall,
                                            int a_iScale)
    {
        double result=0.0;
        if (a_bCall)
            result= Utilities.round(a_dUnderlyingAssetPrice-a_dStrikePrice,a_iScale);
        else
            result=Utilities.round(a_dStrikePrice-a_dUnderlyingAssetPrice,a_iScale);

        if (result<0.0) result=0.0;

        return result;
    }

    /**
     * This method calcuates the present-value of a bond from the given set of 
     * cashflows and cashflow yields.
     * @param a_dCashFlow The cashflow from the bond
     * @param a_dCashFlowYield The cashflow yield from the bond
     * @return The present-value (P) of the bond
     */
    public static double calculateBondPresentValue(double[] a_dCashflow,
                                        double a_dCashflowYield)
                                        throws IllegalArgumentException
    {            
        double result=0.0;

        int l_iArrayLength=a_dCashflow.length;        
        double l_dPlaceHolder;
        
        for (int i=0;i<l_iArrayLength;i++)
        {
            l_dPlaceHolder=Math.pow(1.0 + a_dCashflowYield,-(double)(i+1));
            l_dPlaceHolder*=a_dCashflow[i];
            result+=l_dPlaceHolder;
        }//end for loop

        if (l_iArrayLength>0)
            result/=(double)l_iArrayLength;
        
        return result;
    }

    /**
     * This method calcuates the dollar-duration of a bond from the given set of 
     * cashflows and cashflow yields.
     * @param a_dCashFlow The cashflow from the bond
     * @param a_dCashFlowYield The cashflow yield from the bond
     * @return The dollar-duration (K) of the bond
     * same length.
     */
    public static double calculateBondDollarDuration(double[] a_dCashflow,
                                                    double a_dCashflowYield)
    {
        double result=0.0;

        int l_iArrayLength=a_dCashflow.length;        
        double l_dPlaceHolder;
        
        for (int i=0;i<l_iArrayLength;i++)
        {
            l_dPlaceHolder=Math.pow(1.0 + a_dCashflowYield,-(double)(i+2));
            l_dPlaceHolder*=(double)(i+1) * a_dCashflow[i];
            result+=l_dPlaceHolder;
        }//end for loop

        if (l_iArrayLength>0)
            result/=(double)l_iArrayLength;        
        return result;
    }

    /**
     * This method calcuates the dollar-convexivity of a bond from the given set of 
     * cashflows and cashflow yields.
     * @param a_dCashFlow The cashflow from the bond
     * @param a_dCashFlowYield The cashflow yield from the bond
     * @return The dollar-convexivity (Q) of the bond
     */
    public static double calculateBondDollarConvexivity(double[] a_dCashflow,
                                                 double a_dCashflowYield)
                                                 throws IllegalArgumentException
    {
        double result=0.0;
        int l_iArrayLength=a_dCashflow.length;        
        double l_dPlaceHolder;
        
        for (int i=0;i<l_iArrayLength;i++)
        {
            l_dPlaceHolder=Math.pow(1.0 + a_dCashflowYield,-(double)(i+3));
            l_dPlaceHolder*=(double)(i+1)*(double)(i+2) * a_dCashflow[i];
            result+=l_dPlaceHolder;
        }//end for loop

        if (l_iArrayLength>0)
            result/=(double)l_iArrayLength;        
        return result;
    }            
}

PM MAIL   Вверх
hatsumeika
Дата 7.10.2005, 20:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



очень не рекомендую использовать для хранения денег или значений, используемых для вычисления денег double.

Ибо:
Код

System.out.println(0.33 / 11);

0.030000000000000002


лучше использовать BigDecimal.
при этом нужно использовать конструктор BigDecimal(String), а не BigDecimal(double). Т.е. нужно писать BigDecimal("0.1"), а не BigDecimal(0.1)
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
javastic
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Общие вопросы | Следующая тема »


 




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


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

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