Math.ceilを使用してJavaをintに切り上げる


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

なぜまだ4を返すのですか?157/32 = 4.90625、私は切り上げる必要があります、私は周りを見回しましたが、これは正しい方法のようです。

タイプtotalとして試してみましたdoubleが、4.0を取得しました。

何が悪いのですか?

回答:


187

あなたはやっているの157/32は常に丸いダウン整数にお互いに2つの整数を、除算されています。したがって、(int) Math.ceil(...)何もしていません。あなたが望むものを達成するための3つの可能な解決策があります。オプション1またはオプション2の使用をお勧めします。オプション0は使用しないでください。

##オプション0

変換してdoubleに変換するab、除算を使用Math.ceilして希望どおりに機能させることができます。ただし、二重除算は不正確になる可能性があるため、このアプローチの使用はお勧めしません。ダブルスの不正確さの詳細については、この質問を参照してください。

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

##オプション1

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

あなたは行うa / b場合は、常に床とaし、b両方とも整数です。次に、インラインのifステートメントの魔女が、床の代わりに天井を張るべきかどうかをチェックします。したがって、+ 1または+0、除算の剰余がある場合は+1が必要です。a % b == 0残りをチェックします。

##オプション2

このオプションは非常に短いですが、おそらく直感的ではありません。このあまり直感的でないアプローチは、二重除算および比較アプローチよりも高速だと思います。
これは、では機能しないことに注意してくださいb < 0

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

オーバーフローの可能性を減らすために、以下を使用できます。ただし、a = 0およびでは機能しないことに注意してくださいb < 1

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

##「直感的でないアプローチ」の背後にある説明

Java(および他のほとんどのプログラミング言語)で2つの整数を除算すると、常に結果がフロアされます。そう:

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

しかし、私たちは望んfloor(a/b)でいませんがceil(a/b)ウィキペディアの定義とプロットを使用します:ここに画像の説明を入力してください

これらのfloorおよびceil関数のプロットを使用すると、関係を確認できます。

フロア機能 Ceil関数

あなたはそれを見ることができますfloor(x) <= ceil(x)。必要floor(x + s) = ceil(x)です。したがって、を見つける必要がありますs。私たちが1/2 <= s < 1それをとれば、ちょうどいいでしょう(いくつかの数字を試してみて、あなたがそれを見るでしょう、私はこれを証明するのが難しいと思います)。そして1/2 <= (b-1) / b < 1

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

これは本当の証拠ではありませんが、あなたがそれに満足していることを望みます。誰かがそれをよりよく説明できれば私もそれを感謝します。MathOverflowで質問してください。


1
あまり直感的でないアプローチの背後にある直感を説明できれば、それは非常に有利になりますか?私はこれが正しいことを知っています。あなたがどうやってそれに到達したのか、そしてそれが数学的に正しいことをどのように数学的に示すことができるのかを知りたいのです。数学的に解いてみたが、納得できなかった。
Saad Rehman Shah

私の編集に満足していただければ幸いです。これ以上のことはできないと思います:(
martijnn2008

Math.floorとceilは整数の除算に対してのみ正しいと思います。値がdoubleにキャストされる場合、longの除算は正しくありません。カウンターの例は、4611686018427386880/4611686018427387137が床で失敗し、4611686018427386881/4611686018427386880が天井で失敗した
Wouter

2
明確化のポイント:オプション2の2つのサブオプションの結果は、すべてのケースで同一ではありません。aの値がゼロの場合、最初の値は0、2番目の値は1になります(これは、ほとんどのアプリケーションでは正しい答えではありません)。
Sushisource

1
「a = 0でb < 1の場合は機能しないことに注意してください」という意味ではなかったのですか
dantiston


34

157/32そうでない場合は(接尾辞の指定がない限り、全ての数値リテラルは整数であるため、整数除算であり、d二重のためにl長い間)

除算は(4に)切り捨てられてからdouble(4.0)に変換され、次に(4.0に)切り上げられます。

変数を使用すると、それを回避できます

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


7

誰も最も直感的に言及していません:

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

このソリューションは、二重除算の不正確さを修正します。


1
Math.roundが長く戻る
Zulqurnain Jutt

ありがとう@ZulqurnainJutt、キャストを追加
IG Pascual


3

たとえば、2つの整数を除算する場合

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

結果はでありint、その値はでa除算されb、ゼロに向かって丸められます。結果はすでに丸められているため、ceil()何もしません。この丸めはfloor()、負の無限大に丸めると同じではないことに注意してください。したがって、3/2等しい1(およびfloor(1.5)等しいが1.0(-3)/2等しい-1(ただしfloor(-1.5)等しい-2.0))。

これが重要なのa/bは、常にと同じである場合、asをfloor(a / (double) b)実装するだけceil()でよいa/bため-( (-a) / b)です。

取得の提案ceil(a/b)から

int n = (a + b - 1) / b;、またはと同等a / b + (b - 1) / b、または(a - 1) / b + 1

ので、作品ceil(a/b)より1つの大きいが常にあるfloor(a/b)場合を除き、a/b全体の数です。したがって、が整数でない限り、次の整数に(または過去に)バンプする必要がありますa/b。これを追加する1 - 1 / bと追加されます。整数の場合、それらを次の整数に押し上げることはありません。他のすべてのために、それはします。

うわぁ。うまくいけば、それは理にかなっています。それを説明するより数学的にエレガントな方法があると私は確信しています。


2

また、数値を整数から実数に変換するには、ドットを追加できます。

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

そして、(157/32)の結果も本物になります。;)



1

以下の解決策で質問を確認してください。

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

ここで、分子に1.0を掛けると、答えが得られます。

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

0

ダブルを使用して次のようにキャストします

Math.ceil((double)value) またはのように

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

0

/デフォルトでは、Javaはフロアディビジョンのみを提供します。しかし、私たちは床の観点から天井を書くことができます。どれどれ:

整数yはの形式で記述できますy == q*k+r。四捨五入する階分割(ここfloor)の定義によればr

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

切り上げる天井分割(ここceil)のr₁

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

代わりにr+1使用できる場所r₁

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


次に、最初の方程式を3番目の方程式に代入しq

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

最後に、任意の整数与えられたいくつかのために、、、私たちは持っているがyy = q*k+r+1qkr

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

これで完了です。お役に立てれば。


私はこれが正しいと確信していますが、これのポイントは明確にすることceilなので、特に私たちが整数のセル、つまりr1 = kをとっている場合、最初の定義からそのように定義されている理由は明確ではありません。エッジケースはこれについてトリッキーですので、もう少し詳しく説明する必要があると思います。
Luigi Plinge

@LuigiPlinge分割操作のコンテキストでの床と天井の本質的な違いのため、私には導出を単純にすることはできません。エッジケースに集中する必要はないと思います。整数を分解して床と天井の定義を統一しようとするのは自然なことです。その結果、証明は3つのステップにすぎず、結論は「1つの前の償却ステップ、次に1つの絶対ステップ前進」としておおまかに記憶できます。
-ShellayLee

0

double値を切り上げる方法は2つあります。

  1. Math.ceil
  2. Math.floor

回答4.90625を4にしたい場合は、Math.floorを使用し、回答4.90625を5にしたい場合は、Math.ceilを使用できます。

そのために以下のコードを参照できます。

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);

    }

}

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

より一般的な

(a-1)/b +1 

私はこれはうまくいくと思いますが、元のバージョンがうまくいかなかった理由を本当に説明していません。
Teepeemm 2014年

しかし、それは= 0、およびb <1のために仕事をしませんのでご注意ください
IGパスクアル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.