Почему при объявлении числа с плавающей запятой требуется буква «f»?

87

Пример:

float timeRemaining = 0.58f;

Почему fв конце этого числа обязательно стоит?

Томас
источник
4
Наверное, потому что иначе это трактовалось бы как double.
Uwe Keim
2
0.58 (без суффикса f) - это литерал типа double, и нельзя присвоить значение типа double float, как нельзя присвоить значение int строке. Однако вы можете присвоить значение с плавающей запятой для double, потому что здесь вы расширяете (C # неявно преобразует это за вас, поскольку точность не будет потеряна).
Дэйв Нью
@AVD Хотя это дубликат, приведенный выше заголовок вопроса не дает вашей ссылки в списке возможных дубликатов. Так что, по крайней мере, это может повысить ценность с точки зрения большего количества критериев поиска.
Адам Хулдсворт
1
@AdamHouldsworth - вся дорога идет к double & int .
adatapost

Ответы:

97

Ваше объявление float состоит из двух частей:

  1. Он объявляет, что переменная timeRemainingимеет тип float.
  2. Он присваивает значение 0.58этой переменной.

Проблема возникает в части 2.

Правая часть оценивается сама по себе. Согласно спецификации C # число, содержащее десятичную точку без суффикса, интерпретируется как double.

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

Причина в том, что значение, используемое компилятором, на самом деле не 0,58, а значение с плавающей запятой, наиболее близкое к 0,58, которое составляет 0,57999999999999978655962351581366 ... для doubleи точно 0,579999946057796478271484375 для float.

Строго говоря, fэто не обязательно. Вы можете избежать использования fсуффикса, приведя значение к float:

float timeRemaining = (float)0.58;
Джеффри Сакс
источник
4
Два вопроса, как вы пришли к номеру 0.579999946057796478271484375 и как он будет (float) 0.58работать? Вы сказали ранее, что преобразования нет, потому что информация может быть потеряна, тогда почему приведение будет работать?
SexyBeast
8
1. Я использовал калькулятор Windows, который использует до 34 цифр. 2. Я сказал, что неявного преобразования нет. Приведение - это явное преобразование, поэтому вы явно указываете компилятору, что хотите преобразование, и это разрешено.
Джеффри Сакс
Интересно ... Надеюсь, вы все еще следите за этой публикацией. Я познакомился с суффиксами, когда раньше сталкивался с буквой m в просматриваемой веб-трансляции. Как вы заставили калькулятор Windows отображать .58 как значение с плавающей запятой? Я попробовал пару кнопок, которые выглядели так, как будто они заставили это случиться, но нет ... продолжал получать 0,58. Я должен уважать человека, который так хорошо знает свои инструменты ...
user1585204 01
Я понимаю, что F означает float, но почему? Когда вы объявили переменную, она объявляется как float, поэтому при компиляции она должна знать, что это значение является float. И при двойном объявлении тоже самое. Это не имеет смысла, потому что у вас есть float myvalue = 2.4; теперь при компиляции компилятор уже должен знать, что float - это тип переменной. Я что-то упускаю? Благодарность!
Фрэнк Г.
@FrankG. Две причины: 1. Выражение 2.4везде интерпретируется как двойное. 2. Неявные сужающие преобразования (например, из double в float) не допускаются. Если вы хотите сделать исключение из этих правил, то у вас должна быть очень веская причина. Сохранение одного нажатия клавиши вряд ли будет достаточно.
Джеффри Сакс
36

Поскольку существует несколько числовых типов , которые компилятор может использовать для представления значения 0.58: float, doubleи decimal. Если вы не согласны с тем, что компилятор выберет один за вас, вы должны устранить неоднозначность.

В документации для doubleуказано, что если вы сами не укажете тип, компилятор всегда выберет doubleтип любого действительного числового литерала:

По умолчанию реальный числовой литерал справа от оператора присваивания обрабатывается как двойной. Однако, если вы хотите, чтобы целое число рассматривалось как двойное, используйте суффикс d или D.

Добавление суффикса fсоздает float; суффикс dсоздает double; суффикс mсоздает decimal. Все они также работают в верхнем регистре.

Однако этого все еще недостаточно, чтобы объяснить, почему это не компилируется:

float timeRemaining = 0.58;

Недостающая половина ответа заключается в том, что преобразование из double 0.58к float timeRemainingпотенциально теряет информацию, так что компилятор отказывается применять его в неявном виде . Если вы добавляете явное приведение, преобразование выполняется; если вы добавите fсуффикс, преобразование не потребуется. В обоих случаях код будет компилироваться.

Джон
источник
но как это может быть двойное или десятичное, если переменная является плавающей, я что-то упускаю
Томас
@BlazArt Да, предложение, в котором говорится, что компилятор даже не пытается проверить цель назначения. Причина этого кроется в выборе дизайна, сделанном командой компиляторов. Я предполагаю, что лучше всего быть явным перед лицом возможной путаницы, или было бы слишком дорого реализовывать, вместо того, чтобы просто иметь два правила, одно для intи одно для double.
Адам Хулдсворт
@BlazArt: Только что закончил обдумывать ответ, пожалуйста, посмотрите еще раз.
Джон
1
Что касается потери информации, можете ли вы мне сказать, как на самом деле выполняется приведение в соответствии с форматом IEEE 754, в котором они хранятся? Double имеет более высокую точность, значит ли это, что приведение от float к double всегда будет работать, например double a = 0.69f;?
SexyBeast
@Cupidvogel: Да, спецификация гарантирует (в §6.1.2), что «другие неявные числовые преобразования никогда не теряют никакой информации», что среди прочего включает преобразование floatв double.
Джон
2

Проблема в том, что .NET, чтобы разрешить выполнение некоторых типов неявных операций с участием floatи double, необходимо либо явно указать, что должно происходить во всех сценариях, включающих смешанные операнды, либо разрешить неявные преобразования между типами, которые будут выполняться в одном только направление; Microsoft решила последовать примеру Java, выбрав направление, которое иногда способствует точности, но часто жертвует правильностью и в целом создает проблемы.

Практически во всех случаях double значение, наиболее близкое к определенной числовой величине, и присвоить его a, floatбудет получено floatзначение, наиболее близкое к той же величине. Есть несколько угловых случаев, например, значение 9,007,199,791,611,905; наилучшим floatпредставлением будет 9 007 200 328 482 816 (что меньше на 536 870 911), но приведение лучшего doubleпредставления (т.е. 9 007 199 791 611 904) floatдаст 9 007 199 254 740 992 (что на 536 870 913). В целом, однако, преобразование наилучшего doubleпредставления некоторой величины в floatлибо дает наилучшее возможное floatпредставление, либо одно из двух представлений, которые по существу одинаково хороши.

Обратите внимание, что это желаемое поведение применимо даже в крайних случаях; например, наилучшее floatпредставление величины 10 ^ 308 соответствует floatпредставлению, полученному преобразованием наилучшего doubleпредставления этой величины. Аналогичным образом, наилучшее floatпредставление 10 ^ 309 соответствует floatпредставлению, полученному путем преобразования наилучшего doubleпредставления этой величины.

К сожалению, преобразования в направлении, не требующем явного приведения, редко бывают настолько точными. Преобразование лучшегоfloat представления значения в doubleредко дает что-либо особенно близкое к наилучшему doubleпредставлению этого значения, а в некоторых случаях результат может отличаться на сотни порядков (например, преобразование наилучшего floatпредставления 10 ^ 40 в doubleдаст значение, которое больше, чем лучшее doubleпредставление 10 ^ 300.

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

суперкар
источник