Math.ceil kullanarak Java'yı bir int'e yuvarlama


102
int total = (int) Math.ceil(157/32);

Neden hala 4 döndürüyor? 157/32 = 4.90625, Yuvarlamam gerekiyor, etrafa baktım ve bu doğru yöntem gibi görünüyor.

Tip totalolarak denedim doubleama 4.0 aldım.

Neyi yanlış yapıyorum?

Yanıtlar:


189

157/32Her zaman aşağı yuvarlanmış bir tamsayı ile sonuçlanan iki tamsayıyı birbiriyle bölerek yapıyorsunuz . Bu nedenle (int) Math.ceil(...)hiçbir şey yapmıyor. İstediğinizi elde etmek için üç olası çözüm vardır. Ben tavsiye birini kullanarak seçeneği 1 veya 2. seçeneği . Lütfen DEĞİL kullanmak seçeneği 0 .

## Seçenek 0

aVe bbir ikiye dönüştürün ve bölmeyi ve Math.ceilçalışmasını istediğiniz gibi kullanabilirsiniz. Bununla birlikte, bu yaklaşımın kullanılmasını kesinlikle önermiyorum, çünkü çift bölme kesin olmayabilir. Çiftlerin belirsizliği hakkında daha fazla bilgi edinmek için bu soruya bakın .

int n = (int) Math.ceil((double) a / b));

##Seçenek 1

int n = a / b + ((a % b == 0) ? 0 : 1); 

Her a / bzaman zemin if ave bikisi de tamsayı ile yaparsınız . O zaman bir satır içi if-ifadesi cadı, yer yerine tavana geçmeniz gerekip gerekmediğini kontrol eder. Yani +1 veya +0, eğer bölümde bir kalan varsa + 1'e ihtiyacınız var.a % b == 0kalanı kontrol eder.

##Seçenek 2

Bu seçenek çok kısadır, ancak daha az sezgisel olabilir. Bu daha az sezgisel yaklaşımın ikili bölme ve karşılaştırma yaklaşımından daha hızlı olacağını düşünüyorum:
Lütfen bunun işe yaramadığını unutmayın b < 0.

int n = (a + b - 1) / b;

Taşma olasılığını azaltmak için aşağıdakileri kullanabilirsiniz. Ancak lütfen a = 0ve için çalışmadığını unutmayın b < 1.

int n = (a - 1) / b + 1;

## "Daha az sezgisel yaklaşım" ın arkasındaki açıklama

Java'da (ve diğer birçok programlama dilinde) iki tamsayıyı bölmek her zaman sonucu ortaya çıkaracaktır. Yani:

int a, b;
int result = a/b (is the same as floor(a/b) )

Ama istemiyoruz floor(a/b)ama Wikipedia'dakiceil(a/b) tanımları ve çizimleri kullanarak :görüntü açıklamasını buraya girin

Zemin ve tavan fonksiyonunun bu grafikleriyle ilişkiyi görebilirsiniz.

Kat işlevi Tavan işlevi

Bunu görebilirsin floor(x) <= ceil(x). İhtiyacımız var floor(x + s) = ceil(x). Yani bulmamız gerekiyor s. Eğer bunu 1/2 <= s < 1doğru kabul edersek (bazı rakamları deneyin ve göreceksiniz ki, bunu kanıtlamakta kendimde zorlanıyorum). Ve 1/2 <= (b-1) / b < 1böylece

ceil(a/b) = floor(a/b + s)
          = floor(a/b + (b-1)/b)
          = floor( (a+b-1)/b) )

Bu gerçek bir kanıt değil, ama umarım tatmin olmuşsunuzdur. Birisi daha iyi açıklayabilirse, ben de minnettar olurum. Belki MathOverflow'da sorabilirsiniz .


1
Daha az sezgisel yaklaşımın arkasındaki sezgiyi açıklayabilirseniz bu büyük bir iyilik olur mu? Bunun doğru olduğunu biliyorum, buna nasıl ulaştığınızı ve matematiksel olarak doğru olduğunu nasıl gösterebilirim bilmek istiyorum. Matematiksel olarak çözmeye çalıştım, ikna olmadım.
Saad Rehman Shah

Umarım düzenlememden memnunsunuzdur, daha iyisini yapamam sanırım :(
martijnn2008

Math.floor ve ceil'in, değerler ikiye katlandığında uzun bölme için değil, yalnızca tamsayı bölme için doğru olduğunu varsayıyorum. Sayaç örnekleri 4611686018427386880/4611686018427387137 zeminde başarısız ve 4611686018427386881/4611686018427386880 tavanda başarısız
Wouter

2
Açıklığa kavuşturmak için bir nokta: 2. seçeneğin iki alt seçeneğinin sonuçları her durumda aynı değildir. A için sıfır değeri, ilkinde 0 ve ikincide 1 sağlayacaktır (çoğu uygulama için bu doğru cevap değildir).
Sushisource

1
"Ancak, a = 0 ve b için işe yaramadığını lütfen unutmayın < 1
dantiston


35

157/32bir tamsayı bölümüdür çünkü tüm sayısal değişmez değerler, bir son ek ile aksi belirtilmedikçe ( uzun diçin çift liçin) tam sayıdır

bölme, ikiye (4.0) dönüştürülmeden önce aşağı yuvarlanır (4'e) ve daha sonra yukarı yuvarlanır (4.0'a)

bir değişken kullanıyorsanız bundan kaçınabilirsiniz

double a1=157;
double a2=32;
int total = (int) Math.ceil(a1/a2);


7

Hiç kimse en sezgisel olandan bahsetmedi:

int x = (int) Math.round(Math.ceil((double) 157 / 32));

Bu çözüm, çift bölme belirsizliğini düzeltir .


1
Math.round uzun döndü
Zulqurnain Jutt

Teşekkürler @ZulqurnainJutt, oyuncu kadrosu eklendi
IG Pascual

4

Java'da .0 eklemek onu iki katına çıkarır ...

int total = (int) Math.ceil(157.0 / 32.0);

3

İki tam sayıyı bölerken, örneğin,

int c = (int) a / (int) b;

sonuç, intdeğeri abölünerek bsıfıra yuvarlanan bir an olur. Sonuç zaten yuvarlanmış olduğu ceil()için hiçbir şey yapmaz. Bu yuvarlamanın, floor()negatif sonsuza yuvarlanan ile aynı olmadığına dikkat edin . Yani, 3/2eşittir 1(ve floor(1.5)eşittir 1.0, ancak (-3)/2eşittir -1(ancak floor(-1.5)eşittir)-2.0 ).

Çünkü eğer bu önemlidir a/bhep aynıydı floor(a / (double) b)o zaman sadece uygulamak, ceil()bir a/bşekilde-( (-a) / b) .

Alma önerisi ceil(a/b)dan

int n = (a + b - 1) / b;, eşdeğerdir a / b + (b - 1) / bveya(a - 1) / b + 1

çalışır çünkü tam sayı olması dışında ceil(a/b)her zaman bir büyüktür . Yani, bir tam sayı olmadığı sürece , onu bir sonraki tam sayıya çarpmak (veya geçmek) istersiniz . Eklemefloor(a/b)a/ba/b1 - 1 / b bunu yapacak. Tam sayılar için, onları bir sonraki tam sayıya pek itmez. Diğer her şey için olacak.

Eyvah. Umarım bu mantıklıdır. Eminim bunu açıklamanın matematiksel olarak daha zarif bir yolu vardır.


2

Ayrıca bir sayıyı tam sayıdan gerçek sayıya dönüştürmek için bir nokta ekleyebilirsiniz:

int total = (int) Math.ceil(157/32.);

Ve (157/32.) 'Nin sonucu da gerçek olacaktır. ;)



1

Sorunuz için aşağıdaki çözümü kontrol edin:

int total = (int) Math.ceil(157/32);

Burada Numerator'u 1.0 ile çarpmalısınız, o zaman cevabınızı verecektir.

int total = (int) Math.ceil(157*1.0/32);

0

Beğenmek için çift kullanın

Math.ceil((double)value) ya da beğen

Math.ceil((double)value1/(double)value2);

0

Java, /varsayılan olarak yalnızca kat bölümü sağlar . Ancak tavan olarak da yazabiliriz . Bakalım:

yForm ile herhangi bir tam sayı yazılabilir y == q*k+r. Yuvarlanan kat bölme tanımına göre (burada floor) r,

floor(q*k+r, k) == q  , where 0  r  k-1

ve yuvarlanan tavan bölümü (burada ceil) r₁,

ceil(q*k+r₁, k) == q+1  , where 1  r  k

nerede yerini alabilir r+1için r₁:

ceil(q*k+r+1, k) == q+1  , where 0  r  k-1


Sonra üçüncü içine ilk denklemi yerine qalma

ceil(q*k+r+1, k) == floor(q*k+r, k) + 1  , where 0  r  k-1

Son olarak, herhangi bir tamsayı verilen bazıları için , , , elimizdekiyy = q*k+r+1qkr

ceil(y, k) == floor(y-1, k) + 1

Ve bitirdik. Bu yardımcı olur umarım.


Eminim bu doğrudur, ancak bunun amacı açıklığa kavuşturmak olduğundan, neden ceilsezgisel tanımdan bu şekilde tanımlandığı, özellikle bir tamsayının tavanını aldığımız yerde, yani r1 = k, bana açık değil . Uç vakalar bu konuda zor olduğu için, biraz daha açıklanması gerektiğini düşünüyorum.
Luigi Plinge

@LuigiPlinge Bana göre türetme, bölme işlemi bağlamında zemin ve tavan arasındaki içsel farktan dolayı daha basit olamaz. Bence uç duruma odaklanmanıza gerek yok - bir tamsayıyı bölerek döşeme ve tavan tanımlarını birleştirmeye çalıştığınızda bu doğal bir gerçektir. Sonuç olarak, kanıt sadece üç adımdır ve sonuç, kabaca "amorti edilmiş bir adım geri, ardından kesin bir adım ileri" olarak hatırlanabilir.
ShellayLee

0

Çift değerinizi yuvarlayabileceğiniz iki yöntem vardır.

  1. Math.ceil
  2. Math.floor

4.90625 cevabınızı 4 olarak istiyorsanız Math.floor ve 4.90625 cevabınızı 5 olarak istiyorsanız Math.ceil kullanabilirsiniz.

Bunun için aşağıdaki koda başvurabilirsiniz.

public class TestClass {

    public static void main(String[] args) {
        int floorValue = (int) Math.floor((double)157 / 32);
        int ceilValue = (int) Math.ceil((double)157 / 32);
        System.out.println("Floor: "+floorValue);
        System.out.println("Ceil: "+ceilValue);

    }

}

0

Bunun eski bir soru olduğunu biliyorum ama bence, hassasiyet kaybını önlemek için BigDecimal kullanan daha iyi bir yaklaşımımız var . Bu arada, bu çözümü kullanarak birkaç yuvarlama ve ölçekleme stratejisi kullanma olanağımız var.

final var dividend = BigDecimal.valueOf(157);
final var divisor = BigDecimal.valueOf(32);
final var result = dividend.divide(divisor, RoundingMode.CEILING).intValue();

-3
int total = (157-1)/32 + 1

veya daha genel

(a-1)/b +1 

Bunun işe yaradığını düşünüyorum, ancak orijinal versiyonun neden çalışmadığını gerçekten açıklamadınız.
Teepeemm

" Ancak, a = 0 ve b <1 için çalışmadığını lütfen unutmayın "
IG Pascual
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.