Чому ціле ділення в C # повертає ціле число, а не float?


131

Хтось знає, чому ціле ділення в C # повертає ціле число, а не float? Яка ідея стоїть за ним? (Це лише спадщина C / C ++?)

В C #:

float x = 13 / 4;   
//== operator is overridden here to use epsilon compare
if (x == 3.0)
   print 'Hello world';

Результатом цього коду буде:

'Hello world'

Строго кажучи, немає такого поняття, як ціле ділення (поділ за визначенням - це операція, яка виробляє раціональне число, цілі числа - це дуже малий підмножина.)


46
тому що це integerподіл, а не floating pointподіл.
Мисливець Макміллен

це має (в VB.Net) реалізувати по-різному природним математичним способом, де всім результатом ділення є ірраціональне число.
BanditoBunny

3
Я думаю, ви маєте на увазі раціональні числа . Див. Вікіпедія : Поділ двох цілих чисел може призвести до залишку. Для завершення поділу решти система числення розширюється на включення дробів або раціональних чисел, оскільки вони в більшості випадків називаються.
аварія

6
З цієї причини я не прихильник "копіювання синтаксису" мовами. Я родом з VB, думаючи, що "C # є .NET", а не "C # як C". Моя помилка, я здогадуюсь, але в цьому випадку я віддаю перевагу шляху VB. Якщо вони зіткнулися з проблемою створення помилки компілятора при використанні неініціалізованих простих типів (ви навіть не отримуєте попередження в C), то чому б не попередити вас, коли ви присвоюєте цілий поділ плавучому?
darda

Відповіді:


96

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

По-перше, ціле ділення досить трохи швидше, тому, якщо вам потрібен лише результат з цілим числом, варто використовувати більш ефективний алгоритм.

По-друге, існує ряд алгоритмів, які використовують цілочисельний поділ, і якби результат ділення завжди був числом з плаваючою комою, ви будете змушені щоразу обводити результат. Один із прикладів верхньої частини моєї голови - це зміна основи числа. Обчислення кожної цифри передбачає ціле ділення числа разом із залишком, а не ділення числа з плаваючою комою числа.

Через ці (та інші пов'язані з цим) причини ціле ділення призводить до цілого числа. Якщо ви хочете , щоб отримати розподіл з плаваючою точкою з двох цілих чисел ви просто повинні пам'ятати , щоб кинути один до double/ float/ decimal.


5
У архіві VB.Net .Net архітектор прийняв ще одне рішення: / - завжди плаваючий поділ, \ - цілочисельний поділ, тож це різновид непослідовності, за винятком випадків, якщо ви вважаєте спадщину C ++;
BanditoBunny

4
Ви можете визначити, під час компіляції, чи /буде оператор виконувати ціле чи поділ з плаваючою комою (якщо ви не використовуєте динамічний). Якщо це важко для вас , щоб зрозуміти це , тому що ви робите так багато на цій одній лінії, то я запропонував би ламати цю лінію на кілька рядків так , щоб його легше зрозуміти, операнди чи цілі числа або типу з плаваючою крапкою. Майбутні читачі вашого коду, ймовірно, оцінять його.
Сервіс

5
Особисто мені здається проблематичним те, що мені завжди доводиться думати, що таке змінні, на які я ділюсь, я вважаю це марним використанням моєї уваги.
BanditoBunny

8
@pelesl Як це було б величезною суттєвою зміною зробити те, за що астрономічна кількість програм буде порушена, я можу з повною впевненістю сказати, що це ніколи не відбудеться в C #. Це та річ, яку потрібно робити з першого дня мовою чи зовсім не робити.
Сервіс

2
@Servy: Є багато подібних речей у C, C ++ та C #. Особисто я думаю, що C # була б кращою мовою, якби був інший оператор для цілого поділу, і, щоб уникнути законного кодового виходу дивовижної поведінки, int/intоператор був просто незаконним [з діагностикою, яка вказує, що код повинен видавати операнд або використовувати інший оператор, залежно від того, яка поведінка була бажаною]. Чи були доступні ще якісь добрі послідовності лексем для цілого поділу, можливо, використання /цієї мети можна буде знехтувати , але я не знаю, що було б практично.
supercat

77

Див. Специфікацію C # . Існує три типи операторів поділу

  • Ціле ділення
  • Поділ з плаваючою комою
  • Десятковий поділ

У вашому випадку у нас є підрозділ Integer із застосованими наступними правилами:

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

Я думаю, що причина, чому C # використовує цей тип поділу для цілих чисел (деякі мови повертають плаваючий результат), є апаратним шляхом - поділ на цілі числа швидше і простіше.


Які мови повертають плаваючий результат? @SergeyBerezovskiy
Ilaria

40

Кожен тип даних здатний перевантажувати кожного оператора. Якщо і чисельник, і знаменник є цілими числами, цілочисельний тип виконає операцію ділення, і він поверне цілий тип. Якщо ви хочете поділу з плаваючою точкою, перед тим, як розділити їх, потрібно ввести одне чи більше число до типів з плаваючою точкою. Наприклад:

int x = 13;
int y = 4;
float x = (float)y / (float)z;

або, якщо ви використовуєте літерали:

float x = 13f / 4f;

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


1
+1 для згадки про те, що для поділу плаваючої точки потрібно плавати лише один термін.
Ксінаріз

Очевидно, ваше твердження про точність є правильним у контексті навчання та в тому, щоб його не складно зрозуміти. Оскільки ми повинні бути максимально точними у своїх робочих місцях, я все-таки хочу уточнити точність: Відповідно до IEE 754-1985, ви можете отримати точний результат (хоча це, як правило, не так). Ви можете отримати точний результат, коли значення обчислення були представлені точними раніше, а результат - просто кажучи, - сума повноважень 2. Незважаючи на те, що в цих особливих випадках не може бути найкращою практикою покладатися на цю точність.
Л. Монті

Додавання щодо точності: Швидкість отримання точного результату різко покращується, оскільки результат близький до 1 або -1. Це може бути трохи заплутано, що ця поширеність все ще залишається 0, оскільки існує нескінченна кількість та кінцева кількість результатів, які можна точно представити. :)
Л. Монті

1
@ L.Monty спасибі за те, що виховує це. Я дізнався більше про плаваючі точки з часу написання цієї відповіді, і точка, яку ви робите, справедлива. Технічно я все ще сказав, що моє твердження «плаваючі точки не є точними» є прийнятним, в тому сенсі, що те, що щось може бути точним, іноді не означає, що це в цілому є точним. Як кажуть, зламаний годинник є правильним двічі на день, але я ніколи не називав би один точний прилад. Я насправді досить здивований, що це та частина, яка вас турбувала більше, ніж моя думка про те, що десятковий тип є точним.
Стівен Доггарт

Десяткові знаки є неточними з усіх тих же причин, що є плаваючими; це просто те, що поплавці є базовою-2, а десяткові - базовою-10. Наприклад, десятковий тип не може точно утримувати точне значення 1/3.
Стівен Доггарт

11

Оскільки ви не використовуєте який - або суфікс, літерали 13і 4інтерпретуються як ціле:

Посібник :

Якщо буквальне не має суфікса, тобто перший з цих типів , в яких його значення може бути представлено: int, uint, long, ulong.

Таким чином, оскільки ви оголосите 13цілим числом, буде виконуватися ціле ділення:

Посібник :

Для операції форми x / y застосовується роздільна роздільна здатність перевантаження оператора для вибору конкретної реалізації оператора. Операнди перетворюються на типи параметрів вибраного оператора, а тип результату - тип повернення оператора.

Попередньо визначені оператори поділу перелічені нижче. Усі оператори обчислюють коефіцієнт x і y.

Ціле ділення:

int operator /(int x, int y);
uint operator /(uint x, uint y);
long operator /(long x, long y);
ulong operator /(ulong x, ulong y);

І так відбувається округлення вниз:

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

Якщо ви зробите наступне:

int x = 13f / 4f;

Ви отримаєте помилку компілятора, оскільки поділ з плаваючою комою ( /оператор 13f) призводить до поплавця, який не можна привласнювати неявно.

Якщо ви хочете, щоб поділ був поділом з плаваючою комою, вам доведеться зробити результат поплавком:

float x = 13 / 4;

Зауважте, що ви все одно розділите цілі числа, які буде неявно закинуто плавати: результат буде 3.0. Щоб явно оголосити операнди як плаваючі, використовуючи fсуфікс ( 13f, 4f).


+1 для пояснення того, що відповідь ви можете мати як плаваючий, але все-таки робити ціле ділення. Крім того, ще один поширений спосіб, який я бачив, щоб змусити ділити плаваючу крапку - це помножити перший член ділення на 1.0.
Ксінаріз

8

Це просто основна операція .

Згадайте, коли ви навчились ділити. На початку ми вирішили 9/6 = 1 with remainder 3.

9 / 6 == 1  //true
9 % 6 == 3 // true

Для отримання цих значень використовується / -оператор у поєднанні з% -оператором.


6

Може бути корисним:

double a = 5.0/2.0;   
Console.WriteLine (a);      // 2.5

double b = 5/2;   
Console.WriteLine (b);      // 2

int c = 5/2;   
Console.WriteLine (c);      // 2

double d = 5f/2f;   
Console.WriteLine (d);      // 2.5

Спробуйте додати кілька пояснень для своєї відповіді
NetStarter

Останній вираз видасть 2.5, ні 2.
Лассе В. Карлсен

Так, неправильне використання. Дякую.
eozten

4

Результат завжди буде такого типу, який має більший діапазон чисельника та знаменника. Винятки становлять байт і короткий, які виробляють int (Int32).

var a = (byte)5 / (byte)2;  // 2 (Int32)
var b = (short)5 / (byte)2; // 2 (Int32)
var c = 5 / 2;              // 2 (Int32)
var d = 5 / 2U;             // 2 (UInt32)
var e = 5L / 2U;            // 2 (Int64)
var f = 5L / 2UL;           // 2 (UInt64)
var g = 5F / 2UL;           // 2.5 (Single/float)
var h = 5F / 2D;            // 2.5 (Double)
var i = 5.0 / 2F;           // 2.5 (Double)
var j = 5M / 2;             // 2.5 (Decimal)
var k = 5M / 2F;            // Not allowed

Немає неявного перетворення між типами з плаваючою комою та десятковим типом, тому поділ між ними не допускається. Ви повинні чітко подати та вирішити, який саме ви хочете (Десятичний має більшу точність та менший діапазон порівняно з типами з плаваючою комою).

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