C # 'da tamsayı bölme neden bir tamsayı döndürür ve kayan nokta döndürmez?


131

C # 'da tamsayı bölümünün neden bir float değil de tamsayı döndürdüğünü bilen var mı? Arkasındaki fikir nedir? (Yalnızca bir C / C ++ mirası mı?)

C # dilinde:

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

Bu kodun sonucu şöyle olacaktır:

'Hello world'

Açıkçası, tamsayı bölme diye bir şey yoktur (tanıma göre bölme, rasyonel bir sayı üreten bir işlemdir, tamsayılar çok küçük bir alt kümedir.)


46
çünkü integerbölünme değil floating pointbölünmedir.
Hunter McMillen

(VB.Net'te) bölme işleminin tüm sonucunun irrasyonel bir sayı olduğu doğal bir matematiksel yolla farklı şekilde uygulanması gerekir.
BanditoBunny 01

3
Rasyonel sayıları kastettiğini düşünüyorum . Wikipedia'ya bakın : İki tam sayıyı bölmek bir kalanla sonuçlanabilir. Kalanın bölünmesini tamamlamak için sayı sistemi, daha genel olarak adlandırıldıkları için kesirleri veya rasyonel sayıları içerecek şekilde genişletilir.
crashmstr

6
Bu nedenle, dillerde "sözdizimini kopyalamayı" sevmiyorum. VB'den geliyorum, "C # .NET'dir" değil, "C # C gibidir". Hatam sanırım, ama bu durumda VB yolunu tercih ediyorum. Eğer başlatılmamış basit türleri kullanırken bir derleyici hatası oluşturma zahmetine girdilerse (C'de bir uyarı bile almazsınız), o zaman neden bir kayan noktaya tamsayı bölümü atarken sizi uyarmıyorsunuz?
darda

Yanıtlar:


96

Yeni programcı için tamsayı bölme yapma hatasını gerçekte kayan nokta bölmesini kullanmak istediklerinde yapmaları yaygın olsa da, gerçek uygulamada tamsayı bölme çok yaygın bir işlemdir. İnsanların onu nadiren kullandığını ve her bölme yaptığınızda her zaman kayan noktalara atmayı hatırlamanız gerekeceğini varsayıyorsanız, yanılıyorsunuz.

Öncelikle, tamsayı bölme biraz daha hızlıdır, bu nedenle yalnızca bir tam sayı sonucuna ihtiyacınız varsa, daha verimli algoritmayı kullanmak isteyebilirsiniz.

İkinci olarak, tamsayı bölme kullanan bir dizi algoritma vardır ve bölmenin sonucu her zaman bir kayan nokta sayısı ise, her seferinde sonucu yuvarlamak zorunda kalırsınız. Kafamın üstünden bir örnek, bir sayının tabanını değiştirmektir. Her bir basamağın hesaplanması, sayının kayan nokta bölümü yerine, bir sayının tamsayı bölümü ile kalanı içerir.

Bu (ve diğer ilgili) nedenlerden dolayı, tamsayı bölme bir tamsayı ile sonuçlanır. İki tam sayının kayan nokta bölümünü elde etmek istiyorsanız, sadece birini a double/ float/' ye dönüştürmeyi hatırlamanız gerekir decimal.


5
VB.Net'te .Net mimarları başka bir karar verdiler: / - her zaman bir kayan bölme, \ - tamsayı bölmesi, bu nedenle C ++ mirasını düşünmeniz dışında, bir tür tutarsızdır;
BanditoBunny 01

4
Derleme zamanında, /operatörün tamsayı mı yoksa kayan nokta bölme mi gerçekleştireceğini (dinamik kullanmıyorsanız) belirleyebilirsiniz. Zor olduğunu ise için size o bir satırda çok yapıyoruz çünkü o yüzden onun daha kolay işlenen de tamsayı veya kayan nokta türleri olup olmadığını anlamaya o birkaç satır içine o çizgiyi kesiliyor öneririm, bunu anlamaya. Kodunuzun gelecekteki okuyucuları muhtemelen takdir edeceklerdir.
12'de 13

5
Kişisel olarak, böldüğüm değişkenlerin ne olduğunu her zaman düşünmek zorunda kalmamı sorunlu buluyorum, bunu dikkatimin savurgan bir kullanımı olarak kabul ediyorum.
BanditoBunny 01

8
@pelesl Astronomik sayıda programın kırılacağı bunu yapmak büyük bir kırılma değişikliği olacağından, C # 'da asla olmayacağını tam bir güvenle söyleyebilirim. Bu, bir dilde 1. günden itibaren yapılması gereken veya hiç yapılmaması gereken türden bir şey.
2013

2
@Servy: C, C ++ ve C # 'da bunun gibi pek çok şey var. Şahsen, tamsayı bölme için farklı bir operatör olsaydı ve meşru kodun şaşırtıcı davranışlar sergilemesini önlemek için, int/intoperatör basitçe yasa dışıydı [kodun bir işlenen atması gerektiğini belirten bir teşhis ile veya hangi davranışın istendiğine bağlı olarak diğer operatör]. Tamsayı bölme için başka iyi bir simge dizisi mevcut olsaydı /, bu amaç için kullanımını reddetmek mümkün olabilirdi , ancak neyin pratik olacağını bilmiyorum.
supercat

77

C # spesifikasyonuna bakın . Üç tür bölüm operatörü vardır

  • Tamsayı bölümü
  • Kayan nokta bölümü
  • Ondalık bölme

Sizin durumunuzda, aşağıdaki kuralların uygulandığı Tamsayı bölmesine sahibiz:

Bölme, sonucu sıfıra yuvarlar ve sonucun mutlak değeri, iki işlenenin bölümünün mutlak değerinden daha küçük olan olası en büyük tam sayıdır. İki işlenen aynı işarete sahipse sonuç sıfır veya pozitif ve iki işlenen zıt işaretlere sahip olduğunda sıfır veya negatiftir.

Bence C # 'ın tamsayılar için bu tür bir bölme kullanmasının nedeni (bazı diller kayan sonuç döndürür) donanımdır - tamsayılar bölümü daha hızlı ve daha basittir.


Hangi diller kayan sonuç döndürür? @SergeyBerezovskiy
Ilaria

40

Her veri türü, her operatörü aşırı yükleyebilir. Hem pay hem de payda tamsayı ise, tamsayı türü bölme işlemini gerçekleştirecek ve bir tam sayı türü döndürecektir. Kayan noktalı bölme istiyorsanız, sayılardan birini veya daha fazlasını, bölmeden önce kayan nokta türlerine çevirmelisiniz. Örneğin:

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

veya değişmez değerler kullanıyorsanız:

float x = 13f / 4f;

Unutmayın, kayan noktalar kesin değildir. Hassasiyeti önemsiyorsanız, bunun yerine ondalık tür gibi bir şey kullanın.


1
Kayan nokta bölmesi yapmak için yalnızca bir terimin kayan olması gerektiğinden bahsettiği için +1.
Xynariz

Açıkçası, kesinlik hakkındaki ifadeniz, öğrenme bağlamında ve anlaşılması karmaşık hale getirilmemesi açısından haklıdır. İşlerimizde olabildiğince kesin olmamız gerektiğinden, yine de kesinliği açıklığa kavuşturmak istiyorum: IEE 754-1985'e göre kesin bir sonuç elde edebilirsiniz (bu çoğunlukla böyle olmasa da). Hesaplama değerleri daha önce tam olarak sunulduğunda ve sonuç - basitçe konuşursak - 2'nin kuvvetleri toplamı olduğunda, kesin bir sonuç elde edebilirsiniz. Bu özel durumlarda bu hassasiyete güvenmek en iyi uygulama olmasa da.
L. Monty

Kesinliğe ek: Sonuç 1 veya -1'e yakın olduğu için kesin bir sonuç elde etme olasılığı büyük ölçüde artar. Sonsuz sayılar ve tam olarak sunulabilecek sonlu sayıda sonuç olduğundan, bu olasılığın hala 0 olarak kalması biraz kafa karıştırıcı olabilir. :)
L. Monty

1
@ L.Monty bunu gündeme getirdiğin için teşekkürler. Bu cevabı yazdığımdan beri kayan puanlar hakkında daha fazla şey öğrendim ve yaptığınız nokta adil. Teknik olarak, yine de "kayan noktalar kesin değildir" ifademin kabul edilebilir olduğunu söyleyebilirim, çünkü bir şeyin bazen doğru olabilmesi, bunun bir bütün olarak kesin olduğu anlamına gelmez. Dedikleri gibi, kırık bir saat günde iki kez doğrudur, ancak birine asla hassas alet demem. Aslında oldukça ondalık tipi benim öneri daha sizi rahatsız parçası olduğunu şaşırdım olduğu kesin.
Steven Doggart

Ondalık sayılar kesin değildir, çünkü kayan sayılarla aynı nedenler; sadece kayan sayılar taban-2 ve ondalık sayılar taban-10'dur. Örneğin, bir ondalık tür kesin olarak 1/3 değerini tutamaz.
Steven Doggart

11

Herhangi bir son ek kullanmadığınız için, değişmezler 13ve 4tamsayı olarak yorumlanır:

Kılavuzu :

Değişmezi hiçbir eki varsa, bu değeri temsil edilebilir ki bu tür ilk sahiptir: int, uint, long, ulong.

Böylece, 13tamsayı olarak ilan ettiğiniz için , tamsayı bölme işlemi gerçekleştirilecektir:

Kılavuzu :

X / y formundaki bir işlem için, belirli bir operatör uygulamasını seçmek için ikili operatör aşırı yük çözümlemesi uygulanır. İşlenenler, seçilen işlecin parametre türlerine dönüştürülür ve sonucun türü, işlecin dönüş türüdür.

Önceden tanımlanmış bölüm operatörleri aşağıda listelenmiştir. Operatörlerin tümü x ve y'nin bölümünü hesaplar.

Tamsayı bölümü:

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

Ve böylece aşağı yuvarlama gerçekleşir:

Bölme, sonucu sıfıra yuvarlar ve sonucun mutlak değeri, iki işlenenin bölümünün mutlak değerinden daha küçük olan olası en büyük tam sayıdır. İki işlenen aynı işarete sahipse sonuç sıfır veya pozitif ve iki işlenen zıt işaretlere sahip olduğunda sıfır veya negatiftir.

Aşağıdakileri yaparsanız:

int x = 13f / 4f;

Bir kayan nokta bölümü ( /operatörü 13f), dolaylı olarak int'e dönüştürülemeyen bir kayan nokta ile sonuçlandığından , bir derleyici hatası alırsınız .

Bölümün bir kayan nokta bölümü olmasını istiyorsanız, sonucu bir kayan nokta yapmanız gerekir:

float x = 13 / 4;

Tam sayıları yine böleceğinize dikkat edin, bu örtük olarak float'a dönüştürülür: sonuç olacaktır 3.0. Son feki ( 13f, 4f) kullanarak işlenenleri float olarak açıkça bildirmek için .


+1, cevabı kayan nokta olarak alabileceğinizi ancak yine de tamsayı bölme yapabileceğinizi açıklamak için. Ek olarak, kayan noktalı bölmeyi zorlamak için gördüğüm diğer bir yaygın yol, bölmenin ilk terimini ile çarpmaktır 1.0.
Xynariz

8

Bu sadece basit bir işlem .

Bölmeyi öğrendiğiniz zamanı hatırlayın. Başlangıçta çözdük 9/6 = 1 with remainder 3.

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

% -Operator ile birlikte / -operatör bu değerleri almak için kullanılır.


6

Yararlı olabilir:

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

Lütfen cevabınız için bazı açıklamalar eklemeyi deneyin
NetStarter

Son ifade üretecek 2.5, değil 2.
Lasse V.Karlsen

Evet, yanlış yazım. Teşekkürler.
2016

4

Sonuç her zaman pay ve payda için daha geniş bir aralığa sahip türden olacaktır. İstisnalar bayt ve kısadır ve int (Int32) üretir.

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

Kayan nokta türleri ve ondalık tür arasında örtük dönüştürme yoktur, bu nedenle aralarında bölmeye izin verilmez. Hangisini istediğinizi açıkça çevirmeniz ve karar vermeniz gerekir (Ondalık, kayan nokta türlerine kıyasla daha fazla hassasiyete ve daha küçük bir aralığa sahiptir).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.