Java числа, човешко закръгляване [затворено]

Обзалагам се, че имаше подобен въпрос тук на SO, просто не можах да го намеря. Въпросът е за коригиране на неточност на математиката в Java, когато получа например число като 235.00000002 или 9875.999999997. По дяволите, за човек тези две всъщност означават 235 и 9876, когато е даден определен праг на точност, нека го наречем "5 нули или деветки".

Но не става въпрос само за цифрите вдясно от десетичната запетая, същото правило трябва да важи и за числа като 23500000001.0 и 5699999999.0

Някакви идеи/библиотеки?

Актуализация: Изглежда, че сгреших, като казах, че е известна тема и е въпрос на минути, докато някой изскочи с известна библиотека. И така, ето логиката, която очаквам: За дадено число, когато се срещнат N последователни нули или деветки в низовото му представяне, тогава тези нули и деветки се закръглят заедно с останалата част от числото вдясно от най-десния "0" или " 9". Например, когато N=5, числото 239999995.546 става 240000000.0, числото 34.0000007 става 34, както и 340000.007 става 340000.

Актуализация 2: Това се случва, когато бързаме или не обръщаме достатъчно внимание на въпроса. съжалявам Говоря за "човешко" закръгляване. Добър пример би бил сравняването на изхода на df и df -h на linux кутия. Що се отнася до „неточността“, за която говорех, моля, изпълнете следния код:

double d = 1000.0;
double mult = 0.12351665;
System.out.println(d * mult / mult);

Отговорът определено не е този, който бихте показали в пазарска количка. В моя случай ситуацията е още по-лоша, не става въпрос само за парите, с които се занимавам, те могат да бъдат всякакви - проценти, големи числа, малки дроби и всички те са резултат от относително тежки математически изчисления. Така че говоря за закръгляването, което има смисъл за човек.

Вече приключих с кода, но все още има шанс някой да го е направил по-добре.


person andbi    schedule 26.04.2014    source източник
comment
Изглежда, че тук няма истински въпрос.   -  person Oliver Charlesworth    schedule 26.04.2014
comment
@OliCharlesworth, а какво пречи да е въпросът?   -  person andbi    schedule 26.04.2014
comment
Имате нужда от по-точна дефиниция на вашия въпрос. Опитайте се да дефинирате общото правило за закръгляване (според изискванията, които имате). След това, ако имате проблеми с прилагането му, публикувайте го тук и помолете за помощ. Вашият въпрос, както е сега, е изключително неясен.   -  person barak manos    schedule 26.04.2014
comment
Добре, разбрах, просто си помислих, че това е доста очевиден и добре известен проблем с известно решение. Ще пренапиша въпроса.   -  person andbi    schedule 26.04.2014
comment
какво общо има с неточността, каквото и да разбирате под това?   -  person Denis Tulskiy    schedule 26.04.2014
comment
Защо 239999995.546 е по-лошо от 239989995.546? Но може би нещо като този въпрос ще ви помогне: stackoverflow.com/questions/7906996/   -  person Sebastian Höffner    schedule 26.04.2014
comment
И току-що осъзнавам, че вашите правила за закръгляване не са последователни: Защо 240000000.0 запазва .0, докато 340000 и 34 не го правят? Също така научната нотация, предоставена от Formatter %e, може да ви помогне, тя дава 2.400000e+08 за 239999995.546   -  person Sebastian Höffner    schedule 26.04.2014
comment
@SebastianHöffner, актуализирах въпроса отново)) Що се отнася до несъответствието - забравете за .0, то може да бъде пропуснато, това е просто начинът да се покаже, че все още е двоен.   -  person andbi    schedule 26.04.2014
comment
След втората ви редакция трябва да призная, че всъщност отговорът, предлагащ BigDecimal, изглежда е точно това, от което се нуждаете. Проблемът, с който се сблъсквате там, е свързан с точността на плаваща запетая, не става дума за форматиране на резултати или нещо подобно. Опитайте 1000.0 * 0.123 / 0.123 и няма да имате проблем с получаването на 999.99999 като резултат. Вие обаче казвате, че сте направили кода, но може би някой би могъл да го направи по-добре - какво ще кажете да ни предоставите кода и ние да видим дали можем да го подобрим?   -  person Sebastian Höffner    schedule 26.04.2014
comment
Моето решение всъщност също изглежда работи добре с вашия пример: System.out.println(simplify(d*mult/mult)); отпечатва 1000.   -  person Sebastian Höffner    schedule 26.04.2014
comment
Чувствам, че все още трябва малко да изясня мнението си тук. Моето решение просто се отпечатва, както поискахте. Но за да разрешите проблема по по-добър начин, е да следвате отговора, даден от @Aleksander, защото той се занимава с основния курс на вашия проблем. Никой никога не е казвал, че трябва да използвате BigDecimal само за валути - и вашите примери за проценти, големи числа, малки дроби и т.н. пасват идеално на приложения за BigDecimals.   -  person Sebastian Höffner    schedule 26.04.2014
comment
@SebastianHöffner, разбира се, че бих използвал BigDecimal, но никога не съм казвал, че аз правя математиката, получавам тези числа чрез уеб услуга, която е извън моя контрол((   -  person andbi    schedule 26.04.2014
comment
Добре, мисля, че вашето изчисление там е било подвеждащо, това предполага по някакъв начин, че правите изчисленията и трябва да се справите с проблемни резултати.   -  person Sebastian Höffner    schedule 26.04.2014
comment
възможен дубликат на Как да закръглим число до n десетични знаци в Java   -  person mmmmmm    schedule 27.04.2014


Отговори (3)


Поиграх си малко с научната нотация, както вече предложих в коментарите. Ето какво измислих:

public static double simplify(Number n) {
    String numberString = String.format("%e", n);
    int indexE = numberString.indexOf('e');
    String baseValue = numberString.substring(0, indexE);
    String exponent = numberString.substring(indexE + 1);
    double base = Double.parseDouble(baseValue);
    int    exp = Integer.parseInt(exponent);

    return base * Math.pow(10, exp);
}

Използвах всички числа, които намерих във вашия въпрос, и добавих отрицателна стойност, за да го тествам.

public static void main(String[] args) {
    Number[] ns = new Number[]{
            239999995.546,
            239989995.546,
               340000.007,
                   34.0000007,
           5699999999.0,
                  235.00000002,
                 9875.999999997,
                -4334.345345,
          23500000001.0,
                    0.30000007,
                   -0.053999949
    };
    DecimalFormat df = new DecimalFormat("0.#####");
    for(Number n : ns) {
        String s = df.format(simplify(n));
        System.out.println("    " + n + " is " + s);
    }
}

Резултатите са:

2.39999995546E8 is 240000000
2.39989995546E8 is 239990000
340000.007 is 340000
34.0000007 is 34
5.699999999E9 is 5700000000
235.00000002 is 235
9875.999999997 is 9876
-4334.345345 is -4334.345
2.3500000001E10 is 23500000000
0.30000007 is 0.3
-0.053999949 is -0.054

Редактиране Коригирах кода да използва двойно, поправих грешката с експоненти ‹ 0 и добавих друг пример. Освен това включих DecimalFormat. Имайте предвид, че добавянето на още # може да промени някои резултати, т.е. -0.053999949 сега ще се показва като -0.054, с повече цифри ще доведе до -0.05399995.

person Sebastian Höffner    schedule 26.04.2014
comment
Изглежда интересно. Но какво се случи с -4334.345345, защо се промени? - person andbi; 26.04.2014
comment
Научната нотация е -4.334345e+03, кодът грабва го разделя на -4.334345 и 3 и изчислява 4.334345 * 10^3 = 4334.345, което след това се превръща в long, следователно десетичните цифри се изхвърлят. Променете типа връщане на double и ще получите по-добри резултати, въпреки че трябва да се борите с много нули след периода. редактиране: и за това вече има отговори: stackoverflow.com /questions/11284938/ - person Sebastian Höffner; 26.04.2014
comment
Ясно, разбрах. Току-що тествано, не работи с 0.30000007 например, но мисля, че разбирам идеята ви, мога да го поправя сам. Благодаря ви за търпението)) - person andbi; 26.04.2014
comment
Проверих го, опитайте +1 вместо +2 за степенна степен, не съм мислил за отрицателни степени. Ще актуализирам отговора си, също за да отразя двойни. - person Sebastian Höffner; 27.04.2014

Вероятно се опитвате да попитате как да съхранявате и боравите с валути. Кратките отговори са: използвайте типа BigDecimal или създайте своя собствена валутна реализация с отделни целочислени полета за евро и центове (или долари, ако предпочитате :)).

Вече е обяснено: Използване на BigDecimal за работа с валути

Защо не използвате Double или Float за представяне на валута?

person Aleksandar Stojadinovic    schedule 26.04.2014
comment
Всъщност не става дума за валути, а за човешко закръгляване на числата. - person andbi; 26.04.2014
comment
Добре, в началото не разбрах въпроса. Аз след актуализацията. - person Aleksandar Stojadinovic; 26.04.2014

Тъй като питате за идеи, какво ще кажете за това: направете N не число от последователни нули или деветки, а праг, при който закръгляването на число все още е близко до първоначалното число. След това преминете от най-малката десетична позиция, като закръгляте числото едно по едно и разделяте полученото число на това, което сте имали преди. Спрете, ако съотношението надвиши вашия праг. Експериментирайте със съотношението и граничните условия, при които закръгляването ви изглежда добре. Използвайте BigDecimal, за да избегнете проблеми с аритметиката с плаваща запетая.

person Denis Tulskiy    schedule 26.04.2014