Всегда ли приведение типа double к float возвращает одно и то же значение? - PullRequest
21 голосов
/ 16 марта 2012

Всегда ли приведение double к float дает один и тот же результат, или могут быть какие-то "различия в округлении"?

Например, x в

float x = (float)0.123456789d;

всегда одно и то же значение?

Как насчет того, чтобы бросить поплавок в удвоение, а затем снова бросить его в поплавок, т.е. (float)(double)someFloat?

В основном интересует, какие результаты есть в C #, но не стесняйтесь поделиться, если у вас есть знания о том, как это работает на других языках.

Ответы [ 6 ]

12 голосов
/ 16 марта 2012

Результаты не должны зависеть от языка, если только язык не отличается от спецификации IEEE.

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

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

6 голосов
/ 16 марта 2012

Если вы понижаете a double до float, вы теряете точность и данные.Повышение float до double - это расширение ;никакие данные не будут потеряны, если они затем будут подвергнуты циклическому отключению ... то есть, если вы не сделаете что-то до значения, перед тем как вернуть его обратно в число с плавающей точкой.точность и точность для диапазона .Поплавки одинарной точности дают вам 32-битную точность;двойная точность дает вам 64-бит.Но они могут представлять значения далеко за пределами границ, которые будет указывать базовая точность.

C # float и double являются значениями IEEE 754.

ЭффективныйТочность мантиссы на 1 бит больше, чем ее кажущийся размер (магия с плавающей запятой).

Некоторые ресурсы CLR для вас:

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

  • David Goldberg.1991. Что должен знать каждый компьютерщик об арифметике с плавающей точкой .ACM Comput.Surv.23, 1 (март 1991), 5-48.DOI = 10.1145 / 103162.103163 http://doi.acm.org/10.1145/103162.103163

    Аннотация Арифметика с плавающей точкой рассматривается многими людьми как эзотерический предмет.Это довольно удивительно, потому что с плавающей точкой в ​​компьютерных системах повсеместно: почти каждый язык имеет тип данных с плавающей точкой;компьютеры от ПК до суперкомпьютеров имеют ускорители с плавающей запятой;большинство компиляторов будут время от времени компилировать алгоритмы с плавающей точкой;и практически каждая операционная система должна реагировать на исключения с плавающей точкой, такие как переполнение.Эта статья представляет учебное пособие по аспектам с плавающей запятой, которые оказывают непосредственное влияние на разработчиков компьютерных систем.Он начинается с предыстории представления с плавающей точкой и ошибки округления, продолжается обсуждением стандарта IEEE с плавающей точкой и заканчивается примерами того, как сборщики компьютерных систем могут лучше поддерживать число с плавающей запятой.

1 голос
/ 21 июля 2012

В некоторых случаях ближайшее float представление к числовому количеству может отличаться от значения, полученного округлением ближайшего double представления до float.Два таких количества - 12 344 321,4999999991 и 12 345 678,50000000093.Целые числа выше и ниже обеих этих величин точно представлены как float, но ближайшая double к каждой из них имеет дробную часть точно 0,5.Поскольку преобразование таких значений double (между 2 ^ 23 и 2 ^ 24, с долей точно 0,5) в float будет округлять до ближайшего четного целого числа;в каждом случае компилятор будет округлять значение, которое было бы ближе к исходному числу.

Обратите внимание, что на практике компилятор, кажется, анализирует числа как double, а затем преобразует в float, поэтому даже если 12344321.4999999991f должен округляться до 12344321f, он вместо этого округляется до 12344322f.Аналогично, 12345678.50000000093f следует округлять до 12345679f, но округлять до 12345678f, поэтому даже в случаях, когда преобразование в double, а затем float теряет точность, таких потерь преобразования невозможно избежать, указав числа непосредственно как float.

Между прочим, значения 12344321.4999999992f и 12345678.50000000094f округлены правильно.

1 голос
/ 16 марта 2012

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

Операции с плавающей точкой, особенно приведение, всегда являются предметом усечения / округления и любого другого типа аппроксимации .

0 голосов
/ 16 марта 2012

Числа с плавающей точкой в ​​C # хранятся в формате IEEE 754 (http://en.wikipedia.org/wiki/IEEE_754).. Этот формат состоит из двух частей: цифр и показателя степени. Двойные числа содержат 52 цифры, а плавающие - 23 цифры. Основа составляет 2, а не десять.Таким образом, для вашего примера выше (0.123456789), цифры будут 111010110111100110100010101 (двоичное представление 123456789). Это 27 цифр, что удобно в двойном, но не в плавающей точке, так что да, точность будет потеряна в раунде-трип-преобразование.

С другой стороны, если бы ваш номер был 0.123456, цифры были бы 11110001001000000 (17 цифр), что удобно вписывается в число с плавающей или десятичной дробью, поэтому вы не потеряете точность в раундетройной отлив.

0 голосов
/ 16 марта 2012

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

...