Может ли каждый поплавок выражаться точно как двойной?
Может ли любое возможное значение переменной float
быть представлено точно в переменной double
?
Другими словами, для всех возможных значений X
будет выполнено следующее:
float f1 = X;
double d = f1;
float f2 = (float)d;
if(f1 == f2)
System.out.println("Success!");
else
System.out.println("Failure!");
Мое подозрение в том, что исключение не существует, или если оно есть только для случая с краем (например, +/- бесконечность или NaN).
Изменить. Исходная формулировка вопроса была путаной (было указано два пути: на один из них ответят "нет", на другой ответ будет "да" за тот же ответ). Я переформулировал его так, чтобы он соответствовал названию вопроса.
Ответы
Ответ 1
Да.
Доказательство путем перечисления всех возможных случаев:
public class TestDoubleFloat {
public static void main(String[] args) {
for (long i = Integer.MIN_VALUE; i <= Integer.MAX_VALUE; i++) {
float f1 = Float.intBitsToFloat((int) i);
double d = (double) f1;
float f2 = (float) d;
if (f1 != f2) {
if (Float.isNaN(f1) && Float.isNaN(f2)) {
continue; // ok, NaN
}
fail("oops: " + f1 + " != " + f2);
}
}
}
}
заканчивается через 12 секунд на моей машине. 32 бита малы.
Ответ 2
В теории нет такого значения, поэтому "да", каждый поплавок должен быть представлен как двойной. Преобразование из float в double должно включать в себя просто наложение четырех байтов 00 на конец - они хранятся в том же формате, только с полями различного размера.
Ответ 3
Да, поплавки - это подмножество удвоений. Оба поплавка и двойники имеют вид (знак * a * 2 ^ b). Разница между поплавками и удвоениями - это количество бит в а и b. Поскольку в двухлокальных номерах доступно больше бит, назначение значения с плавающей точкой для двойного действия означает добавление дополнительных 0 бит.
Ответ 4
Как уже сказано, "нет". Но это фактически "да" для самого вопроса, т.е. Каждый float может быть точно выражен как двойной. Смешение.:)
Ответ 5
Если я правильно прочитал спецификацию языка (и, как все остальные подтверждают), такого значения нет.
То есть каждый утверждает, что он поддерживает только стандартные значения IEEE 754, поэтому отбрасывание между ними не должно меняться, кроме как в памяти.
(пояснение: не было бы изменений, если бы значение было достаточно маленьким, чтобы удерживаться в поплавке, очевидно, если бы значение было слишком большим, чтобы бит был скомпонован в float, то отбрасывание из double в float было бы приводят к потере точности.)
Ответ 6
@KenG: Этот код:
float a = 0.1F
println "a=${a}"
double d = a
println "d=${d}"
не выполняется, потому что 0,1f не может быть точно представлен. Вопрос состоял в том, что "существует ли значение float, которое не может быть представлено как double", которое этот код не доказывает. Хотя 0.1f не может быть сохранено точно, значение, которое дано (которое не равно 0.1f), может быть сохранено как двойное (что также не будет точно 0,1f). Предполагая FPU Intel, бит-шаблон для a:
0 01111011 10011001100110011001101
и бит-бит для d:
0 01111111011 100110011001100110011010 (далее следует много больше нулей)
который имеет тот же знак, показатель (-4 в обоих случаях) и ту же дробную часть (разделенную пробелами выше). Разница в выходе обусловлена положением второй ненулевой цифры в числе (первая - 1 после точки), которая может быть представлена только двойным. Код, который выводит строковый формат, сохраняет промежуточные значения в памяти и специфичен для float и double (т.е. Существует функция double-to-string и другая float-to-string). Если функция to-string была оптимизирована для использования стека FPU для хранения промежуточных результатов в строковых процессах, то выход был бы одинаковым для float и double, поскольку FPU использует тот же самый более крупный формат (80 бит) для обоих float и двойной.
Нет значений float, которые нельзя хранить одинаково в двойном, то есть набор значений float является подмножеством набора двойных значений.
Ответ 7
Snark: NaN
будет сравнивать по-другому после (или даже раньше) преобразования.
Это, однако, не отменяет уже предоставленных ответов.
Ответ 8
Я взял код, который вы указали, и решил попробовать его на С++, так как я думал, что он может выполнить немного быстрее, и значительно легче выполнить небезопасное кастинг.:-D
Я узнал, что для действительных чисел преобразование работает, и вы получаете точное поразрядное представление после трансляции. Однако для не-чисел, например. 1. # QNAN0 и т.д., Результат будет использовать упрощенное представление не числа, а не точные биты источника. Например:
**** FAILURE **** 2140188725 | 1. # QNAN0 - 0xa0000000 0x7ffa1606
Я запустил unsigned int для float, затем, чтобы удвоить и вернуться к float. Число 2140188725 (0x7F90B035) приводит к NAN, и преобразование в двойное и обратное все еще является NAN, но не тем же самым NAN.
Вот простой код на С++:
typedef unsigned int uint;
for (uint i = 0; i < 0xFFFFFFFF; ++i)
{
float f1 = *(float *)&i;
double d = f1;
float f2 = (float)d;
if(f1 != f2)
printf("**** FAILURE **** %u | %f -- 0x%08x 0x%08x\n", i, f1, f1, f2);
if ((i % 1000000) == 0)
printf("Iteration: %d\n", i);
}
Ответ 9
Ответ на первый вопрос - да, ответ на "другими словами", однако, нет. Если вы измените тест в коде if (!(f1 != f2))
, то ответ на второй вопрос станет да - он будет печатать "Успех" для всех значений с плавающей запятой.
Ответ 10
В теории каждый нормальный сингл может иметь показатель экспоненты и мантиссы, чтобы создать двойной, а затем удалить дополнение и вернуться к исходному синглу.
Когда вы переходите от теории к реальности, это когда у вас будут проблемы. Я не знаю, интересуетесь ли вы теорией или реализацией. Если это реализация, вы можете быстро попасть в неприятности.
IEEE - это ужасный формат, и я понимаю, что он был намеренно сконструирован настолько жестко, что никто не мог его встретить и позволить рынку догнать интеллект (это было в то время назад), что позволило добиться большей конкуренции. Если это правда, это провалилось, так или иначе мы застряли в этой ужасной спецификации. Что-то вроде формата TI намного превосходит реальный мир во многих отношениях. У меня нет связи ни с одной из компаний, ни с одним из этих форматов.
Благодаря этой спецификации очень мало, если любой fpus, который на самом деле встречает его (в аппаратном или даже аппаратном обеспечении, а также в операционной системе), и те, которые часто терпят неудачу в следующем поколении. (google: TestFloat). Проблемы в эти дни, как правило, лежат в int, чтобы плавать и плавать до int, а не одиночные, чтобы удвоить и удвоить до одного, как вы указали выше. Конечно, какая операция будет выполнять fpu для выполнения этого преобразования? Добавить 0? Умножить на 1? Зависит от fpu и компилятора.
Проблема с IEEE, связанная с вашим вопросом выше, заключается в том, что существует несколько вариантов числа, а не число, но многие числа могут быть представлены. Если бы я хотел разбить свой код, я бы начал с минус нуля в надежде, что одна из двух операций преобразует его в плюс-ноль. Тогда я попробую денормалы. И он должен потерпеть неудачу с сигналом nan, но вы назвали это как известное исключение.
Проблема в том, что знак равенства, вот правило номер один о плавающей запятой, никогда не используйте знак равенства. Equals - это сравнение не по сравнению с сравнением значений, если у вас есть два значения, представленные разными способами (например, плюс нуль и минус нуль), сравнение бит не удастся, даже если оно будет одинаковым. Чем больше и меньше, чем в fpu, то равное выполняется с помощью целочисленного alu.
Я понимаю, что вы, вероятно, использовали равное, чтобы объяснить проблему, а не обязательно код, который вы хотели бы преуспеть или потерпеть неудачу.
Ответ 11
Если тип с плавающей запятой рассматривается как представляющий точное значение, то, как отмечали другие плакаты, каждое значение float
представляется как double
, но только несколько значений double
могут быть представлены посредством float
. С другой стороны, если признать, что значения с плавающей запятой являются приближениями, можно понять, что реальная ситуация меняется на противоположную. Если вы используете очень точный инструмент для измерения чего-то, что составляет 3.437 мм, можно правильно описать размер 3,4 мм. если вы используете линейку для измерения объекта как 3,4 мм, было бы неправильно описать его размер как 3.400 мм.
Еще большие проблемы существуют в верхней части диапазона. Существует значение float
, которое представляет: "вычисленное значение превысило 2 ^ 127 на неизвестную сумму", но нет значения double
, которое указывает на такую вещь. Литье "бесконечности" от одного до двойного даст значение "вычисленное значение превысило 2 ^ 1023 на неизвестное количество", которое отключено в разы по сравнению с googol.