Потеря точности - int → float или double
У меня вопрос экзамена, на который я пересматриваю, и вопрос заключается в 4-х знаках.
"В java мы можем назначить int двойному или поплавку". Будет ли это когда-либо потерять информацию и почему?
Я установил это потому, что ints обычно имеют фиксированную длину или размер - точность хранения данных конечна, где хранение информации в плавающей запятой может быть бесконечным, по существу мы теряем информацию из-за этого
Теперь я немного отрывочен относительно того, нахожу ли я здесь подходящие места. Я очень уверен, что он потеряет точность, но я не могу точно сказать, почему. Могу ли я получить помощь, пожалуйста?
Ответы
Ответ 1
В Java Integer использует 32 бита для представления своего значения.
В Java FLOAT использует 23-битную мантиссу, поэтому целые числа, превышающие 2 ^ 23, будут усекаться по наименьшим значащим битам. Например, 33554435 (или 0x200003) будет усечен примерно до 33554432 +/- 4
В Java DOUBLE использует 52-битную мантиссу, поэтому сможет представлять 32-битное целое число без потери данных.
См. также " Плавающая точка" на wikipedia
Ответ 2
Не нужно знать внутреннюю компоновку чисел с плавающей запятой. Все, что вам нужно, это принцип пигментной скважины и знание того, что int
и float
имеют одинаковый размер.
-
int
- это 32-разрядный тип, для которого каждый бит-шаблон представляет собой отдельное целое число, поэтому существуют значения 2 ^ 32 int
.
-
float
- это 32-разрядный тип, поэтому он имеет не более 2 ^ 32 различных значений.
- Некоторые
float
представляют нецелые числа, поэтому значений <2 > 32 float
, представляющих целые числа, меньше.
- Поэтому различные значения
int
будут преобразованы в один и тот же float
(= потеря точности).
Аналогичные рассуждения можно использовать с long
и double
.
Ответ 3
Здесь JLS должен сказать об этом (в нетехническом обсуждении).
Следующие 19 конкретных преобразований для примитивных типов называются расширяющимися примитивными преобразованиями:
-
int
до long
, float
или double
- (оставлено без опускания)
Преобразование значения int
или long
в float
или значения long
в double
может привести к потере точности - то есть результат может потерять часть младшие значащие бит значения. В этом случае полученное значение с плавающей запятой будет правильно округленной версией целочисленного значения с использованием режима IEEE 754, близкого к ближайшему.
Несмотря на то, что может произойти потеря точности, расширение конверсий между примитивными типами никогда не приводит к исключению во время выполнения.
Вот пример расширяющегося преобразования, которое теряет точность:
class Test {
public static void main(String[] args) {
int big = 1234567890;
float approx = big;
System.out.println(big - (int)approx);
}
}
который печатает:
-46
что указывает на то, что во время преобразования из типа int
была потеряна информация, чтобы напечатать float
, потому что значения типа float
не точны до девяти значащих цифр.
Ответ 4
Нет, float
и double
тоже фиксированные длины - они просто используют свои биты по-разному. Подробнее о том, как именно они работают в Руководство по плавающим Poing.
В принципе, вы не можете потерять точность при назначении int
a double
, потому что double
имеет 52 бита точности, что достаточно для хранения всех значений int
. Но float
имеет только 23 бита точности, поэтому он не может точно представить все значения int
, которые больше, чем около 2 ^ 23.
Ответ 5
Возможно, самое ясное объяснение, которое я видел:
http://www.ibm.com/developerworks/java/library/j-math2/index.html
ULP или единица наименьшей точности определяет точность, доступную между любыми двумя значениями float. Поскольку эти значения увеличивают доступную точность, уменьшается.
Например: между 1.0 и 2.0 включительно 8388609 поплавков, между 1,000,000 и 1,000,001 есть 17. В 10 000 000 ULP равно 1.0, поэтому выше этого значения у вас скоро будет несколько значений целочисленных значений для каждого доступного float, следовательно, потеря точности.
Ответ 6
Ваша интуиция верна, вы можете потерять точность при преобразовании int
в float
. Однако это не так просто, как представлено в большинстве других ответов.
В Java FLOAT использует 23-битную мантиссу, поэтому целые числа, превышающие 2 ^ 23, будут усекаться по наименьшим значащим битам. (из сообщения на этой странице)
Не верно.
Пример: здесь есть целое число, большее 2 ^ 23, которое преобразуется в float без потерь:
int i = 33_554_430 * 64; // is greater than 2^23 (and also greater than 2^24); i = 2_147_483_520
float f = i;
System.out.println("result: " + (i - (int) f)); // Prints: result: 0
System.out.println("with i:" + i + ", f:" + f);//Prints: with i:2_147_483_520, f:2.14748352E9
Следовательно, неверно, что целые числа, превышающие 2 ^ 23, усекают наименее значимые биты.
Лучшее объяснение, которое я нашел, находится здесь:
Поплавок в Java 32-бит и представлен:
знак * мантисса * 2 ^ экспоненциальный
знак * (от 0 до 33_554_431) * 2 ^ (от 125 до +127)
Источник: http://www.ibm.com/developerworks/java/library/j-math2/index.html
Почему это проблема?
Это оставляет впечатление, что вы можете определить, есть ли потеря точности из int для float , просто посмотрев, насколько велика int.
Я особенно видел вопросы Java-экзамена, где задается вопрос, будет ли большой int преобразовывать в float без потерь.
Кроме того, иногда люди склонны думать, что будет потеря точности от int до float:
когда значение int больше: 1_234_567_890 не верно (см. вышеприведенный пример)
когда int больше, чем: 2 показатель 23 (равно: 8_388_608) не верно
когда int больше, чем: 2 экспонента 24 (равно: 16_777_216) не верно
Заключение
Преобразования от достаточно больших ints к floats МОГУТ потерять точность.
Невозможно определить, будет ли потеря просто выглядящей по тому, насколько велика int (т.е. Не пытаться углубиться в фактическое представление с плавающей точкой).
Ответ 7
Есть две причины, по которым присваивание int двойному или поплавка может потерять точность:
- Есть определенные числа, которые просто не могут быть представлены как double/float, поэтому они заканчиваются аппроксимированными
- Большие целые числа могут содержать слишком большую точность в значимых для аренды цифрах.
Ответ 8
В этих примерах я использую Java.
Используйте такую функцию, чтобы проверить на потерю точности при литье из int в float
static boolean checkPrecisionLossToFloat(int val)
{
if(val < 0)
{
val = -val;
}
// 8 is the bit-width of the exponent for single-precision
return Integer.numberOfLeadingZeros(val) + Integer.numberOfTrailingZeros(val) < 8;
}
Используйте функцию, подобную этой, чтобы проверить потерю точности при литье из длинного в двойное
static boolean checkPrecisionLossToDouble(long val)
{
if(val < 0)
{
val = -val;
}
// 11 is the bit-width for the exponent in double-precision
return Long.numberOfLeadingZeros(val) + Long.numberOfTrailingZeros(val) < 11;
}
Используйте такую функцию, чтобы проверить потерю точности при литье из long to float
static boolean checkPrecisionLossToFloat(long val)
{
if(val < 0)
{
val = -val;
}
// 8 + 32
return Long.numberOfLeadingZeros(val) + Long.numberOfTrailingZeros(val) < 40;
}
Для каждой из этих функций возвращение true означает, что приведение этого интегрального значения в значение с плавающей запятой приведет к потере точности.
Листинг для float потеряет точность, если интегральное значение имеет более 24 значащих бит.
Литье в два раза будет терять точность, если интегральное значение имеет более 53 значащих бит.