java float double Почему точность не теряется [дублировать]

попробуйте это.

document.getElementById("edName").required = true;
13
задан Amro 6 July 2013 в 18:15
поделиться

9 ответов

1
ответ дан Amro 17 August 2018 в 23:00
поделиться

Как уже объяснялось, все поплавки могут быть точно представлены как двойные, и причина вашей проблемы в том, что System.out.println выполняет некоторое округление при отображении значения float или double, но методология округления не является то же самое в обоих случаях.

Чтобы увидеть точное значение поплавка, вы можете использовать BigDecimal:

float f = 125.32f;
System.out.println("value of f = " + new BigDecimal(f));
double d = (double) 125.32f;
System.out.println("value of d = " + new BigDecimal(d));

, который выводит:

value of f = 125.31999969482421875
value of d = 125.31999969482421875
1
ответ дан assylias 17 August 2018 в 23:00
поделиться

Оба являются тем, что Microsoft называет «приблизительными типами данных числа».

Есть причина. Поплавок имеет точность 7 цифр и двойную 15. Но я видел, что это случалось много раз, что 8.0 - 1.0 - 6.999999999. Это связано с тем, что они не гарантируют точно представлять дробь десятичного числа.

Если вам нужна абсолютная неизменная точность, пойдите с десятичным или интегральным типом.

-1
ответ дан Curt 17 August 2018 в 23:00
поделиться
  • 1
    & Quot; приблизительная & Quot; довольно неудобный способ описать, что такое число с плавающей точкой IEEE, так как точно представляет собой очень точно определенный набор чисел. – Marko Topolnik 6 July 2013 в 17:38
  • 2
    Это вообще не затрагивает вопрос. – Zong 6 July 2013 в 17:39
  • 3
    «Тип данных приблизительного номера» это то, что Microsoft называет float и real: msdn.microsoft.com/en-us/library/ms173773.aspx – Curt 6 July 2013 в 17:43
  • 4
    @Curt Если кто-то начинает называть тип с плавающей точкой «реальным», то сначала нужно упомянуть, что он приблизительный: как тип данных для хранения реальных чисел, это, безусловно, так. Я не думаю, что документация от Microsoft о типах с плавающей точкой в ​​SQL должна считаться подходящим справочным материалом для обсуждения с плавающей запятой либо сама по себе, либо в Java. – Pascal Cuoq 7 July 2013 в 13:06
  • 5
    @MarkoTopolnik: С точки зрения кода, который выполняет низкоуровневые вычисления, поплавки IEEE являются точно определенными типами. Однако, с точки зрения кода consumer , если программа считывает два значения float (скажем, x = 1.0 и y = 10.0) и вычисляет float z=x/y;, гораздо более вероятно, что программист считает z как обладающее несовершенным представлением значения введенной фракции, чем точное представление фракции 13421773/134217728. – supercat 4 September 2013 в 08:54

Значение a float не изменяется при преобразовании в double. Существует разница в отображаемых цифрах, потому что требуется большее количество цифр для различения значения double от его соседей, которое требуется для документации Java . Это документация для toString, которая отсылается (через несколько ссылок) из документации для println.

Точное значение для 125.32f - 125.31999969482421875. Двумя соседними float значениями являются 125.3199920654296875 и 125.32000732421875. Обратите внимание, что 125.32 ближе к 125.31999969482421875, чем к любому из соседей. Поэтому, отображая «125.32», Java отображает достаточно цифр, чтобы преобразование с десятичной цифры на float воспроизводило значение float, переданное в println.

Два соседних double значения 125.31999969482421875: 125.3199996948242045391452847979962825775146484375 и 125.3199996948242329608547152020037174224853515625. Обратите внимание, что 125.32 ближе к последнему соседу, чем к исходному значению. Поэтому печать «125.32» не содержит достаточно цифр для отличия исходного значения. Java должна печатать больше цифр, чтобы убедиться, что преобразование с отображаемой цифры обратно в double воспроизводит значение параметра double, переданное в println.

10
ответ дан Eric Postpischil 17 August 2018 в 23:00
поделиться
  • 1
    в единственной точности последовательность чисел, точно представляемых вокруг этого числа: 0x42faa3d6, 0x42faa3d7, 0x42faa3d8 (в шестнадцатеричной нотации с шагом в один бит). Все, что находится между ними, округляется до ближайшего числа. То же самое можно сказать и о двойной точности. btw исправьте меня, если я ошибаюсь, но одноточность позволяет примерно 9 значащих десятичных цифр, поэтому все лишние цифры, которые вы показали, отбрасываются и игнорируются – Amro 7 July 2013 в 05:17
  • 2
    @Amro: дополнительные цифры не отбрасываются или игнорируются. Арифметика, выполняемая объектами с плавающей точкой, ведет себя так, как если бы они имели точно полные значения, включая значения, которые я показал. Это потому, что у них есть эти полные ценности; в спецификации IEEE 754 указано, что они имеют точно такие значения и другие. Они не , используемые компьютером как десятичные приближения с несколькими цифрами. Компьютер вычисляет с ними в двоичном формате, и они имеют точно указанные значения. – Eric Postpischil 7 July 2013 в 12:51
  • 3
    hmm Я предполагаю, что эта деталь всегда меня путала, можете ли вы прокомментировать это: stackoverflow.com/questions/4227145/… ? – Amro 7 July 2013 в 16:42
  • 4
    @Amro: стандарт IEEE 754 определяет, какие значения представлены объектами с плавающей запятой. Стандарт не определяет понятия значимых десятичных цифр для двоичного формата с плавающей запятой. Просто нет оснований утверждать, что двоичный объект с плавающей точкой IEEE-754 имеет некоторое количество значащих десятичных цифр. Объект имеет ровно одно значение, которое точно указано, и оно не содержит информации о значимости. Это точное значение. Стандарт позволяет реализациям ограничивать количество цифр, которые они производят при преобразовании значения в десятичную цифру ... – Eric Postpischil 8 July 2013 в 04:10
  • 5
    @supercat: Я не комментирую здесь, должен ли какой-либо конкретный человек использовать плавающую точку каким-либо определенным образом. Я просто заявляю, что стандарт IEEE 754 достаточно четко указывает, что значение с плавающей запятой, отличное от NaN, представляет собой одно значение, а стандарт точно определяет это значение. Считаете ли вы, что это хорошо или плохо, не имеет никакого значения для того, что стандарт говорит об этом, и что результаты операций с плавающей запятой определяются в терминах этих значений. Поскольку реализации IEEE 754 следуют этим правилам, анализ их поведения должен следовать этим правилам. – Eric Postpischil 26 November 2013 в 22:55
3
ответ дан harold 17 August 2018 в 23:00
поделиться

Преобразование из float в double является расширяющимся преобразованием , как , указанным JLS . Расширяющееся преобразование определяется как инъективное отображение меньшего набора в его супермножество. Поэтому число, представляемое , не изменяется после преобразования с float в double.

Дополнительная информация о вашем обновленном вопросе

В вашем обновлении вы добавили пример, который должен продемонстрировать, что число изменилось. Однако это только показывает, что строковое представление номера номера изменилось, что действительно связано с дополнительной точностью, полученной путем преобразования в double. Обратите внимание, что ваш первый вывод - это просто округление второго выхода. Как указано в Double.toString,

Должна быть хотя бы одна цифра для представления дробной части, а кроме того, столько же, сколько только столько цифр, сколько необходимо, чтобы однозначно различать значение аргумента из смежных значений типа double.

Поскольку смежные значения в типе double намного ближе, чем в float, для соответствия этому правлению требуется больше цифр .

4
ответ дан Marko Topolnik 17 August 2018 в 23:00
поделиться
  • 1
    Пожалуйста, прочтите оригинальный вопрос, на который мой оригинальный ответ является полностью подходящим ответом, здесь . С тех пор вопрос был существенно обновлен. – Marko Topolnik 4 June 2014 в 08:18
  1. Когда вы конвертируете float в double, потеря информации отсутствует. Каждый float может быть представлен точно как double.
  2. С другой стороны, ни одно десятичное представление, напечатанное System.out.println, не является точным значением для числа. Для точного десятичного представления может потребоваться до 760 десятичных цифр. Вместо этого System.out.println печатает точно число десятичных цифр, которые позволяют анализировать десятичное представление обратно в исходное float или double. Есть больше double s, поэтому при печати один из них System.out.println должен печатать больше цифр, прежде чем представление станет однозначным.
11
ответ дан Pascal Cuoq 17 August 2018 в 23:00
поделиться
  • 1
    На самом деле нет double, эквивалентного лучшему float представлению 3.5E + 38. Сравнение такого значения с лучшим поплавковым представлением любого другого значения выше 3.5E + 38 будет указывать на то, что значения неотличимы - не обязательно очень информативны, но правильны. С другой стороны, преобразование этого значения в double приведет к ошибочному сравнению большего , чем лучшее double представление всех значений ниже 1.798E + 308 - ошибка сотен заказов . – supercat 4 September 2013 в 09:02
  • 2
    @supercat Этот ответ о преобразовании float в double. «Лучшее float-представление 3.5E + 38» - +inf, а float +inf преобразуется в double +inf без потери точности (это то же inf!). Как вы понимаете, что бесконечность - это ваша проблема, а не проблема конверсии. Значение с плавающей запятой (здесь +inf) представляет только одно значение (здесь, бесконечность). Вы могли бы сделать тот же аргумент с 1-ульп-интервалами вокруг 1.0f и double 1.0, и аргумент был бы аналогичным образом неактуальен. Это float, то есть одно значение, которое преобразуется в double. – Pascal Cuoq 4 September 2013 в 09:21
  • 3
    @supercat См. «Некоторые распространенные заблуждения (2)» в lipforge.ens-lyon.fr/www/crlibm/documents/cern.pdf . De Dinerchin говорит только о конечных значениях с плавающей запятой, но то же самое относится к inf. Значение inf с плавающей запятой является not "диапазоном значений, содержащим 3.5E+38", это одно значение, бесконечность. Аппроксимация при переходе 3.5E38 в +inf уже произошла до преобразования в double и не мешает тому, чтобы преобразование в double было точным. – Pascal Cuoq 4 September 2013 в 09:25
  • 4
    Значение с плавающей запятой эффективно инкапсулирует два понятия: что можно сказать о произведенных им вычислениях и что нужно вводить в вычисления в будущем. Учитывая выражение, подобное float2=float1/0.625f, если float1 равно 62.5f, то значение float2 указывает, что арифметический результат последней операции был между 13421772.5 / 134217728 и 13421773.5 / 134217728 и что в будущем использование float2 будет использовать точное значение 13421773/134217728. Если float1 равно 3.4E38, значение float2 будет указывать, что ... – supercat 4 September 2013 в 16:10
  • 5
    ... арифметический результат превысил 3,4028E38 на неизвестную величину, и что будущие использования float2 будут считать его бесконечным. Если бы кто-то хотел знать, можно ли окончательно считать, что арифметический результат вычислений, который дал float2, более чем 0,11 или 1,7E + 308, приведение второго операнда каждого сравнения к float будет правильно сообщать, что они не могут , Приведение float2 в double предполагает, что они могли бы. – supercat 4 September 2013 в 16:24

Представление значений изменяется из-за контрактов методов, которые преобразуют числовые значения в String, соответственно java.lang.Float#toString(float) и java.lang.Double#toString(double), тогда как фактическое значение остается неизменным. В Javadoc есть общая часть обоих вышеупомянутых методов, которая разрабатывает требования к представлению значений String:

. Должна быть хотя бы одна цифра для представления дробной части, а за ее пределами , но только столько цифр, сколько требуется, чтобы однозначно отличать значение аргумента от смежных значений

. Чтобы проиллюстрировать сходство значимых частей для значений обоих типов, можно выполнить следующий фрагмент :

package com.my.sandbox.numbers;

public class FloatToDoubleConversion {

    public static void main(String[] args) {
        float f = 125.32f;
        floatToBits(f);
        double d = (double) f;
        doubleToBits(d);
    }

    private static void floatToBits(float floatValue) {
        System.out.println();
        System.out.println("Float.");
        System.out.println("String representation of float: " + floatValue);
        int bits = Float.floatToIntBits(floatValue);
        int sign = bits >>> 31;
        int exponent = (bits >>> 23 & ((1 << 8) - 1)) - ((1 << 7) - 1);
        int mantissa = bits & ((1 << 23) - 1);
        System.out.println("Bytes: " + Long.toBinaryString(Float.floatToIntBits(floatValue)));
        System.out.println("Sign: " + Long.toBinaryString(sign));
        System.out.println("Exponent: " + Long.toBinaryString(exponent));
        System.out.println("Mantissa: " + Long.toBinaryString(mantissa));
        System.out.println("Back from parts: " + Float.intBitsToFloat((sign << 31) | (exponent + ((1 << 7) - 1)) << 23 | mantissa));
        System.out.println(10D);
    }

    private static void doubleToBits(double doubleValue) {
        System.out.println();
        System.out.println("Double.");
        System.out.println("String representation of double: " + doubleValue);
        long bits = Double.doubleToLongBits(doubleValue);
        long sign = bits >>> 63;
        long exponent = (bits >>> 52 & ((1 << 11) - 1)) - ((1 << 10) - 1);
        long mantissa = bits & ((1L << 52) - 1);
        System.out.println("Bytes: " + Long.toBinaryString(Double.doubleToLongBits(doubleValue)));
        System.out.println("Sign: " + Long.toBinaryString(sign));
        System.out.println("Exponent: " + Long.toBinaryString(exponent));
        System.out.println("Mantissa: " + Long.toBinaryString(mantissa));
        System.out.println("Back from parts: " + Double.longBitsToDouble((sign << 63) | (exponent + ((1 << 10) - 1)) << 52 | mantissa));
    }
}

В моей среде выход:

Float.
String representation of float: 125.32
Bytes: 1000010111110101010001111010111
Sign: 0
Exponent: 110
Mantissa: 11110101010001111010111
Back from parts: 125.32

Double.
String representation of double: 125.31999969482422
Bytes: 100000001011111010101000111101011100000000000000000000000000000
Sign: 0
Exponent: 110
Mantissa: 1111010101000111101011100000000000000000000000000000
Back from parts: 125.31999969482422

Таким образом, вы можете видеть, что знак значений, экспонент одинаковы, а его мантисса была расширена

Используемая логика извлечения чисел с плавающей запятой: 1 и 2 .

g7]
0
ответ дан Pavel 17 August 2018 в 23:00
поделиться
0
ответ дан sekhar beri 17 August 2018 в 23:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: