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

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

Дело не в том, что вы на самом деле получаете дополнительную точность – это то, что поплавок не точно отражает число, на которое вы стремились первоначально. Двойник точно представляет исходный поплавок; toString показывает «дополнительные» данные, которые уже присутствовали.

Например (и эти цифры не правы, я просто делаю все), поддерживая вас:

 float f = 0.1F; double d = f; 

Тогда значение f может быть точно 0,100000234523. d будет иметь точно такое же значение, но когда вы преобразуете его в строку, он будет «доверять», чтобы он был точным с более высокой точностью, поэтому не будет завершен как можно раньше, и вы увидите «дополнительные цифры», которые были уже там, но скрыто от вас.

Когда вы конвертируете в строку и обратно, вы получаете двойное значение, которое ближе к строковому значению, чем исходный поплавок, но это хорошо, если вы действительно считаете, что строковое значение – это то, что вы действительно хотели.

Вы уверены, что 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 должно быть значением float, ближайшим к x; или, если два значения float одинаково близки к x, то f должен быть одним из них, а младший значащий бит значения f должен быть 0.

Сегодня я столкнулся с этой проблемой и не мог использовать рефакторинг для BigDecimal, потому что проект действительно огромный. Однако я нашел решение, используя

 Float result = new Float(5623.23) Double doubleResult = new FloatingDecimal(result.floatValue()).doubleValue() 

И это работает.

Обратите внимание, что вызов result.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 и double вместо Float и Double, используйте следующее:

 public static double getFloatAsDouble(float value) { return Double.valueOf(Float.valueOf(value).toString()).doubleValue(); } 

Поплавки по своей природе неточны и всегда имеют аккуратные округлые «проблемы». Если точность важна, вы можете рассмотреть возможность рефакторинга приложения для использования Decimal или BigDecimal.

Да, поплавки вычисляются быстрее, чем десятичные числа из-за поддержки процессора. Однако вы хотите быстро или точно?

Для информации это относится к пункту 48 – Избегайте плавать и удваивать, когда требуются точные значения, эффективного Java 2-го издания Джошуа Блоха. Эта книга – варенье, наполненное хорошими вещами, и, безусловно, стоит посмотреть.

Это работает?

 float flt = 145.664454; Double dbl = 0.0; dbl += flt; 
  • сравнение значений float / double с использованием оператора ==
  • C: Приведение минимального 32-битного целого числа (-2147483648) к float дает положительное число (2147483648.0)
  • Как преобразовать целое число в float в Java?
  • Как получить десятичную часть поплавка?
  • Как хранится плавающая запятая? Когда это имеет значение?
  • проблема с плавающей запятой в R?
  • Каким детерминированным является неточность с плавающей запятой?
  • Float и двойной тип данных в Java
  • Является ли добавление и умножение с плавающей запятой ассоциативным?
  • Почему я вижу двойную переменную, инициализированную некоторым значением, например 21.4, как 21.399999618530273?
  • Как преобразовать строку в float?
  • Interesting Posts

    Поиск захвата HttpServletResponseWrapper

    Как разобрать 20-значный номер с помощью JavaScript и jQuery

    Разница между scanf (“% c”, & c) и scanf (“% c”, & c)

    Регистрация jQuery щелчка, первый и второй клик

    Вложенные функции в Java

    Многопроектные тестовые зависимости с gradleиентом

    Как найти исходный процесс неизвестного всплывающего окна в Windows 7

    В Java, когда я должен создать проверенное исключение и когда это должно быть исключение во время выполнения?

    Получите прокрутку с двумя пальцами на сенсорной панели ноутбука Windows (например, на macbook)?

    C # Установленное соединение было прервано программным обеспечением вашей хост-машины

    Как настроить SSH, чтобы он не пытался автоматически загружать все файлы удостоверений?

    Работа с событием AppDomain.AssemblyResolve

    Вставка в unordered_set с пользовательской hash-функцией

    Как добавить дополнительные коннекторы (клей) в форму в слове или powerpoint?

    Бесплатный API штрих-кода для .NET

    Давайте будем гением компьютера.