Чому при оголошенні плаваючих знаків потрібна буква "f"?


86

Приклад:

float timeRemaining = 0.58f;

Чому fв кінці цього числа потрібно ввести?


4
Можливо, тому що інакше це трактувалось би як double.
Уве Кейм,

2
0.58 (без суфікса f) - це літерал типу double, і не можна призначати подвійне значення плаваючому коду, як і не можна присвоювати int-значення рядку. Однак ви можете призначити плаваюче значення подвійному, оскільки тут ви розширюєтесь (C # неявно перетворить це для вас, оскільки жодна точність не буде втрачена).
Дейв Нью


@AVD Незважаючи на те, що це копія, назва запитання вище не містить посилання у списку можливих дублікатів. Тож це може бути додатковою вартістю з точки зору принаймні більшої кількості критеріїв пошуку.
Адам Голдсворт,

1
@AdamHouldsworth - вся дорога йде до double & int .
adatapost

Відповіді:


96

Ваша декларація про поплавок містить дві частини:

  1. Він оголошує, що змінна timeRemainingмає тип float.
  2. Це значення присвоює 0.58цій змінній.

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

Права сторона оцінюється самостійно. Відповідно до специфікації C # число, що містить десяткову крапку, що не має суфікса, інтерпретується як a double.

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

Причина полягає в тому, що значення, яке використовує компілятор, насправді не 0,58, а значення з плаваючою комою, найближче до 0,58, що становить 0,579999999999978655962351581366 ... для 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

Я розумію все, що стосується Float, але чому? Коли ви оголошуєте змінну, вона оголошується як плаваюча, тому при компіляції вона повинна знати, що це значення є плаваючою. І коли ви заявляєте подвійне те саме. Це не має сенсу, оскільки у вас float myvalue = 2,4; тепер при компіляції компілятор вже повинен знати, що float - це тип змінної. Мені чогось не вистачає? Дякую!
Frank G.

@FrankG. Дві причини: 1. Вираз 2.4усюди трактується як подвійний. 2. Неявні перетворення звуження (наприклад, з подвійного на плаваючий) не дозволяються. Якщо ви хочете зробити виняток із цих правил, то у вас повинна бути дуже поважна причина. Збереження 1 натискання клавіші навряд чи буде достатньо хорошим.
Джеффрі Сакс,

36

Оскільки існує кілька числових типів , які компілятор може використовувати для подання значення 0.58: float, doubleі decimal. Якщо ви не в порядку з компілятором, який вибирає для вас, вам доведеться неоднозначно визначити.

У документації для doubleзазначено, що якщо ви не вказуєте тип самі, компілятор завжди вибирає doubleяк тип будь-якого реального числового літералу:

За замовчуванням дійсний числовий літерал праворуч від оператора присвоєння розглядається як подвійний. Однак, якщо ви хочете, щоб ціле число розглядалося як подвійне, використовуйте суфікс d або D.

Додавання суфікса fстворює a float; суфікс dстворює a double; суфікс mстворює a decimal. Усі вони також працюють з великої літери.

Однак цього все ще недостатньо, щоб пояснити, чому це не компілюється:

float timeRemaining = 0.58;

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


але як це може бути подвійним чи десятковим, якщо змінна є плаваючою, мені чогось не вистачає
Томас

@BlazArt Так, речення, в якому зазначено, що компілятор навіть не намагається перевірити ціль призначення. Причина цього буде схована у виборі дизайну, зробленому командою упорядника. Я вважаю, що найкраще бути явним перед можливою плутаниною, або це було занадто дорого, щоб турбуватися реалізацією, а не просто мати два правила, одне для intі одне для double.
Адам Голдсворт,

@BlazArt: Щойно закінчив розгортати відповідь, подивіться ще раз.
Джон,

1
Щодо втрати інформації, чи можете ви сказати мені, як насправді робиться акторський склад щодо формату IEEE 754, в якому вони зберігаються? Подвійний має вищу точність, то чи це означає, що приведення з поплавця на подвійне завжди буде працювати, наприклад double a = 0.69f;?
SexyBeast

@Cupidvogel: Так, специфікація гарантує (у §6.1.2), що "інші неявні числові перетворення ніколи не втрачають жодної інформації", що серед іншого включає перетворення floatв double.
Джон

2

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

Майже у всіх випадках, якщо doubleзначення, яке є найближчим до певної числової величини, і присвоєння йому значення a, floatвийде floatзначення, яке є найближчим до тієї самої величини. Є кілька кутових випадків, наприклад, вартість 9 007 199 991 611 905; найкраще floatпредставлення буде 9 007 200 328 482 816 (що на 536 870 911), але найкраще doubleпредставлення (тобто 9 007 199 991 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.

На жаль, правила перетворення є такими, якими вони є, тому доводиться жити з використанням безглуздих наборів шрифтів та суфіксів при перетворенні значень у "безпечному" напрямку, і бути обережним при неявних наборах шрифтів у небезпечному напрямку, що часто дасть помилкові результати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.