Лучший способ округлить до ближайшего 0,05 в java

Учтите, что налог в размере 10% применяется ко всем товарам, кроме продуктов питания. Кроме того, на ввозимые товары взимается дополнительный налог в размере 5 %.

Если стоимость музыкального компакт-диска составляет 12,49. Налог на товар составит 1.499. Если стоимость импортного флакона духов составляет 47,50, налог на товар составит 7,125.

Существует политика, согласно которой налоги на товар должны быть округлены до ближайшего 0,05. Следовательно, 1,499 следует округлить до 1,5, а 7,125 — до 7,25.

Вышеупомянутое требование округления может быть достигнуто с помощью логики:

    (float) Math.ceil(taxAmount / 0.05f) * 0.05f;

Добавление налога к стоимости товара дает:

музыкальный компакт-диск: 12,49 + 1,5 = 13,99 коробка импортных конфет: 47,50 + 7,25 = 54,65.

Я столкнулся с проблемой для следующего ввода:

Если стоимость импортной коробки конфет составляет 11,85, налог составит 0,5925.

При использовании политики округления налог после округления составит 0,6.

Когда мы добавляем 11,85 + 0,6, которые оба являются числами с плавающей запятой, мы получаем результат как 12,450001. В отличие от других входных данных, этот конкретный ввод дает много знаков после запятой, в отличие от 2 знаков после запятой в других выходных данных.

Я попытался использовать BigDecimal вместо float, чтобы сохранить все значения со шкалой, установленной на 2 десятичных знака. Проблема с этим подходом заключается в том, что bigDecimal в некоторых случаях будет генерировать исключение, если политика округления не указана. Предоставление политики округления для BigDecimal приводит к округлению общей стоимости товара и применимого налога с использованием политики округления, предоставленной для BigDecimal, тем самым изменяя требуемый результат.


person CKing    schedule 05.08.2012    source источник
comment
Re: ваша проблема с BigDecimal, да, весь смысл предоставления политики округления заключается в том, что ее следует использовать. И, очевидно, это изменит результат. Ваша проблема, скорее всего, в том, что вы указали НЕПРАВИЛЬНУЮ политику.   -  person Stephen C    schedule 05.08.2012
comment
Насколько я знаю, ни одна из политик, предоставленных BigDecimal, не соответствует моим требованиям.   -  person CKing    schedule 05.08.2012
comment
Используете ли вы правильное масштабирование? Если да, то вам нужен один из режимов ROUND_HALF_XXX.   -  person Stephen C    schedule 05.08.2012
comment
Я использую шкалу 2. Это правильно?   -  person CKing    schedule 05.08.2012
comment
Удивительно, но использование любого из методов ROUND_HALF_XXX и масштаба 2 решает проблему.   -  person CKing    schedule 06.08.2012


Ответы (3)


Вы можете использовать long вместо double, чтобы использовать double, который вы можете сделать

double d = Math.round(d * 20) / 20.0; // round up to multiple of 0.05

использовать long (как центы)

long l = (l + 3) / 5 * 5;

Хотя часто считается лучшей практикой использовать int, long или BigDecimal, большинство инвестиционных банков и фондов используют double, потому что, как только вы поймете, как их использовать безопасно, они станут проще (чем long) и быстрее (чем BigDecimal) в использовании.

person Peter Lawrey    schedule 05.08.2012
comment
Как мне преобразовать обратно из long в float и узнать точное место, где поставить десятичную точку? Вывод должен быть показан в десятичном формате, а не в длинном. - person CKing; 05.08.2012
comment
Я бы предположил, что в долларе 100 центов. то есть деление на 100,0 перед печатью будет работать. Лично у меня нет проблем с использованием double. Я бы не стал использовать float, если бы действительно не было выбора. - person Peter Lawrey; 05.08.2012
comment
Чтобы преобразовать десятичное значение в long или int, мне нужно будет умножить его на 100? Что делать, если в цене товара после запятой больше двух цифр? - person CKing; 05.08.2012
comment
Что вы подразумеваете под этим: once you understand how to use them safely? Как их безопасно использовать? - person zeller; 05.08.2012
comment
Я оспариваю утверждение, что большинство инвестиционных банков и фондов используют двойное значение. Пожалуйста, предоставьте некоторые доказательства. - person user207421; 05.08.2012
comment
@EJP Честно говоря, все инвестиционные банки, в которых я работал или брал интервью в Лондоне, используют плавающую запятую, некоторые также используют вместо этого целые числа, но это реже (я брал интервью, работал или знаю людей, которые работают почти во всех из них в последние десять лет). Использование BigDecimal считается слишком медленным даже для ситуаций, в которых я подозреваю, что это не так. - person Peter Lawrey; 05.08.2012
comment
Аналогично: stackoverflow.com/questions/22446677/ - person ses; 17.03.2014

Храните денежные суммы в виде целых чисел (например, количество центов).

Это распространенная проблема — Google даст вам много отличных объяснений.

Вот один из них: Почему бы не использовать Double или Float для представления валюты?

person John3136    schedule 05.08.2012
comment
Было бы больше ответа, чтобы сказать, как. В противном случае это скорее комментарий. - person Peter Lawrey; 05.08.2012
comment
Как к чему? Как использовать целое число для хранения денежных сумм? - person John3136; 05.08.2012
comment
Как Best method to round up to the nearest 0.05 in java - person Peter Lawrey; 05.08.2012
comment
Я считаю, что в коде OP есть фундаментальная проблема, которую необходимо исправить, даже не беспокоясь об исходном вопросе об округлении. Не уверен, что комментарий имеет достаточно веса, чтобы донести эту мысль. - person John3136; 05.08.2012
comment
Верно, но указание на то, как легко это сделать, также может помочь. ;) - person Peter Lawrey; 05.08.2012

Кажется, это часть известной проблемы с собеседованием по техническим вопросам. Суть в том, чтобы понять, что вы должны рассчитать налоги и округлить эту сумму до 0,05, чтобы вычислить округление. Я использовал этот отличный скрипт.

import java.math.*

def myRound(BigDecimal d){ 
    def scale = new BigDecimal("0.05")
    def y = d.divide(scale)
    println "y :$y"
//    def q = y.round(new MathContext(0,RoundingMode.UP))
    def q = y.setScale(0,BigDecimal.ROUND_UP)
    println "q :$q (intero)"
    def z = q.multiply(scale)
    println "z :$z"
    return z
}

def taxBy(BigDecimal d,BigDecimal t){ 
    return myRound(d.multiply(t))
}
def importTax(BigDecimal d){ 
    return taxBy(d,new BigDecimal("0.15"))
}

def importBaseTax(BigDecimal b){ 
    return taxBy(d,new BigDecimal("0.05"))
}

ip = new BigDecimal("27.99")
ipt = importTax(ip)

println "${ip}-->${ipt} = ${ip+ipt}"

Я надеюсь, что это будет полезно!

person Endeios    schedule 22.01.2015