Преобразование float в Double без потери точности
у меня есть примитивный поплавок, и мне нужен как примитивный двойник. Просто бросая поплавок в два раза дает мне странную дополнительную точность. Например:
float temp = 14009.35F;
System.out.println(Float.toString(temp)); // Prints 14009.35
System.out.println(Double.toString((double)temp)); // Prints 14009.349609375
однако, если вместо приведения я вывожу float как строку и разбираю строку как двойник, я получаю то, что хочу:
System.out.println(Double.toString(Double.parseDouble(Float.toString(temp))));
// Prints 14009.35
есть ли лучший способ, чем перейти к строке и обратно?
10 ответов:
дело не в том, что ты на самом деле получение дополнительной точности - это то, что поплавок не точно представляет число, к которому вы стремились изначально. Двойник и представление исходного поплавка точно;
toStringпоказывает "дополнительные" данные, которые уже присутствовали.например (и эти цифры не правы, я просто выдумываю) поддержка у вас была:
float f = 0.1F; double d = f;значение
fможет быть именно 0.100000234523.dбудет иметь точно такое же значение, но когда вы преобразуете его в строку, он будет "доверять", что он точен до более высокой точности, поэтому не будет округляться так рано, и вы увидите "дополнительные цифры", которые уже были там, но скрыты от вас.когда вы конвертируете в строку и обратно, вы получаете двойное значение, которое ближе к строковому значению, чем исходное значение float, но это только хорошо если вы действительно верите, что строка ценность-это то, что вы действительно хотели.
вы уверены, что float / double являются подходящими типами для использования здесь вместо
BigDecimal? Если вы пытаетесь использовать числа, которые имеют точные десятичные значения (например, деньги), тоBigDecimalявляется более подходящим типом ИМО.
Я считаю, что преобразование в двоичное представление легче понять эту проблему.
float f = 0.27f; double d2 = (double) f; double d3 = 0.27d; System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(f))); System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d2))); System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d3)));вы можете видеть, что float расширяется до double, добавляя 0s в конец, но что двойное представление 0.27 является "более точным", следовательно, проблема.
111110100010100011110101110001 11111111010001010001111010111000100000000000000000000000000000 11111111010001010001111010111000010100011110101110000101001000
Это связано с договором
Float.toString(float), в которой в частности говорится:сколько цифр должно быть напечатано на дробная часть [...]? Там должно быть не менее одной цифры представляют дробную часть, а кроме того, как многие,но только столько, больше цифр, как это необходимо однозначно отличите значение аргумента от смежные значения типа float. что предположим, что X является точным математическое значение представлены десятичное представление произведенное мимо этот метод для конечного ненулевого аргумент f. тогда F должен быть поплавком значение, ближайшее к x; или, если два поплавка значения одинаково близки к x, затем f должен быть один из них и наименее значительный бит значения f должно быть 0.
я столкнулся с этой проблемой сегодня и не мог использовать рефакторинг для BigDecimal, потому что проект действительно огромен. Однако я нашел решение с помощью
Float result = new Float(5623.23) Double doubleResult = new FloatingDecimal(result.floatValue()).doubleValue()и это работает.
обратите внимание, что называть результатом.doubleValue() возвращает 5623.22998046875
но вызов doubleResult.doubleValue () возвращает правильно 5623.23
но я не совсем уверен, что это правильное решение.
использовать
BigDecimalвместоfloat/double. Есть много чисел, которые не могут быть представлены в виде двоичной с плавающей запятой (например,0.1). Таким образом, вы либо всегда должны округлять результат до известной точности, либо использоватьBigDecimal.см.http://en.wikipedia.org/wiki/Floating_point для получения дополнительной информации.
Я нашел следующее решение:
public static Double getFloatAsDouble(Float fValue) { return Double.valueOf(fValue.toString()); }Если вы используете float и двойной вместо Float и двойной использовать следующий:
public static double getFloatAsDouble(float value) { return Double.valueOf(Float.valueOf(value).toString()).doubleValue(); }
поплавки, по своей природе, неточны и всегда имеют аккуратные округления "вопросы". Если точность важна, то вы можете рассмотреть возможность рефакторинга вашего приложения для использования десятичной или BigDecimal.
да, поплавки вычислительно быстрее, чем десятичные из-за поддержки процессора. Однако, вы хотите быстро или точно?
для информации это происходит по пункту 48-избегайте float и double, когда требуются точные значения, эффективного Java 2nd edition от Joshua Bloch. Эта книга варенье упакованы с хорошим материалом и, безусловно, стоит посмотреть.
Comments