В классе BigDecimal
есть несколько полезных методов, гарантирующих конвертацию без потерь:
byteValueExact()
shortValueExact()
intValueExact()
longValueExact()
Однако методов floatValueExact()
и doubleValueExact()
не существует.
Я прочитал исходный код OpenJDK для методов floatValue()
и doubleValue()
. Оба, кажется, отступают Float.parseFloat()
и Double.parseDouble()
, соответственно, могут возвращать положительную или отрицательную бесконечность. Например, разбор строки 10000 9s вернет положительную бесконечность. Как я понимаю, BigDecimal
нет внутренней концепции бесконечности. Кроме того, анализ строки 100 9 с, как double
дает 1.0E100
, который не является бесконечностью, но теряет точность.
Что такое разумная реализация floatValueExact()
и doubleValueExact()
?
Я думал о double
решении путем объединения BigDecimal.doubleValue()
, BigDecial.toString()
, Double.parseDouble(String)
и Double.toString(double)
, но это выглядит неаккуратно. Я хочу спросить здесь, потому что может (должно быть!) Быть более простое решение.
Чтобы было ясно, мне не нужно высокопроизводительное решение.
источник
Ответы:
От чтения в документации , все это делает с
numTypeValueExact
вариантами, чтобы проверить существование дробной части или если значение слишком велико для числового типа и бросать исключения.Что касается
floatValue()
иdoubleValue()
, аналогичная проверка переполнения выполняется, но вместо того, чтобы генерировать исключение, вместо этого он возвращаетDouble.POSITIVE_INFINITY
илиDouble.NEGATIVE_INFINITY
для удвоений иFloat.POSITIVE_INFINITY
илиFloat.NEGATIVE_INFINITY
для чисел с плавающей запятой.Поэтому наиболее разумная (и самая простая) реализация
exact
методов для float и double должна просто проверять, возвращает ли преобразованиеPOSITIVE_INFINITY
илиNEGATIVE_INFINITY
.Кроме того , помните, что это
BigDecimal
было разработано, чтобы справиться с недостатком точности, возникающим из-за использованияfloat
илиdouble
для больших иррациональных чисел , поэтому, как прокомментировал @JB Nizet , другой проверкой, которую вы можете добавить к вышеприведенному, будет преобразованиеdouble
илиfloat
обратно,BigDecimal
чтобы увидеть, если вы все еще получаете такое же значение. Это должно доказать, что преобразование было правильным.Вот как будет выглядеть такой метод
floatValueExact()
:Использование
compareTo
вместоequals
вышеупомянутого является намеренным, чтобы не стать слишком строгим с проверками.equals
будетBigDecimal
иметь значение true, только если два объекта имеют одинаковое значение и масштаб (размер дробной части десятичной дроби), тогда какcompareTo
пропустит эту разницу, когда это не имеет значения. Так , например2.0
против2.00
.источник
Float.isFinite()
илиFloat.isInfinite()
, но это не обязательно.:)
123.456f
. Я предполагаю, что это происходит из-за разного размера значений и (мантиссы) между 32-разрядным и 64-разрядным двойным. В вашем коде выше, я получаю лучшие результаты с:if (new BigDecimal(result, MathContext.DECIMAL32).equals(decimal)) {
. Это разумное изменение ... или я пропускаю еще один угловой случай значений с плавающей запятой?123.456f
концептуально такой же, как ваш пример 1.0E100. Она поражает меня , что , если нужно , чтобы проверить , что преобразование из BigDecimal -> двоичная с плавающей точкой является точным , то этой потребностью является проблемой; т.е. преобразование не должно быть предусмотрено.