Преобразование 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


есть ли лучший способ, чем перейти к строке и обратно?

1069   10  

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. Эта книга варенье упакованы с хорошим материалом и, безусловно, стоит посмотреть.

это работает?

float flt = 145.664454;

Double dbl = 0.0;
dbl += flt;

Если вам нужно сделать это с фантиками:

Double d = new Float(123f).doubleValue();

Comments

    Ничего не найдено.