1. 程式人生 > >年年有餘之java求餘的技巧集合

年年有餘之java求餘的技巧集合

背景

  傳說裡玉皇大帝派龍王馬上降雨到共光一帶,龍王接到玉皇大帝命令,立馬從海上調水,跑去共光施雲布雨,但粗心又著急的龍王不小心把海里的鯨魚隨著雨水一起降落在了共光,龍王怕玉皇大帝責怪,靈機一動便聲稱他是派魚到共光,希望百姓可以年年有餘,並請求玉皇大帝將這條魚任命為魚神,保佑人間太平可以年年有餘。

                                      年年有餘

java 求餘操作初階

java中也有餘的規範【jls-15.17.3】,廢話不說,直接上程式碼,從中我們可以學到很多技巧:

例1:

        int a = 5%3; // 2
        int b = 5/3; // 1
        System.out.println("5%3 produces " + a +" (note that 5/3 produces " + b + ")");

 

相信大多數人都知道結果了:

          5%3 produces 2 (note that 5/3 produces 1)

java 求餘操作中階

我們知道,正數不僅僅有正整數還有負整數,那麼負數的情況下,會出現什麼變化呢?

例2:

        int c = 5%(-3); // 2
        int d = 5/(-3); // -1
        System.out.println("5%(-3) produces " + c +" (note that 5/(-3) produces " + d + ")");
        int e = (-5)%3; // -2
        int f = (-5)/3; // -1
        System.out.println("(-5)%3 produces " + e +" (note that (-5)/3 produces " + f + ")");
        int g = (-5)%(-3); // -2
        int h = (-5)/(-3); // 1
        System.out.println("(-5)%(-3) produces " + g +" (note that (-5)/(-3) produces " + h + ")");

 

能完全正確得到結果的就很少了吧?

          5%(-3) produces 2 (note that 5/(-3) produces -1)
          (-5)%3 produces -2 (note that (-5)/3 produces -1)
          (-5)%(-3) produces -2 (note that (-5)/(-3) produces 1)

 

 

為什麼求餘的結果是這樣的呢?jls-15.17.3規範告訴我們:

The binary % operator is said to yield the remainder of its operands from an implied division; the left-hand operand is the dividend and the right-hand operand is the divisor.
It follows from this rule that the result of the remainder operation can be negative only if the dividend is negative, and can be positive only if the dividend is positive. Moreover, the magnitude of the result is always less than the magnitude of the divisor.

 

注意:求餘的正負數給dividend(左邊運算元)的符號位一致!

java 求餘操作高階

java求餘操作不但支援整數還支援浮點數

class Test2 {
 public static void main(String[] args) {
 double a = 5.0%3.0; // 2.0
 System.out.println("5.0%3.0 produces " + a);
 double b = 5.0%(-3.0); // 2.0
 System.out.println("5.0%(-3.0) produces " + b);
 double c = (-5.0)%3.0; // -2.0
 System.out.println("(-5.0)%3.0 produces " + c);
 double d = (-5.0)%(-3.0); // -2.0
 System.out.println("(-5.0)%(-3.0) produces " + d);
 }
}

相信很多人可以根據整型的規則,得出正確的結果

5.0%3.0 produces 2.0
5.0%(-3.0) produces 2.0
(-5.0)%3.0 produces -2.0
(-5.0)%(-3.0) produces -2.0

補充一下,浮點型的求餘有一些特殊的規則:

The result of a floating-point remainder operation as computed by the % operator is not the same as that produced by the remainder operation defined by IEEE 754. The IEEE 754 remainder operation computes the remainder from a rounding division, not a truncating division, and so its behavior is not analogous to that of the usual integer remainder operator. Instead, the Java programming language defines % on floating-point operations to behave in a manner analogous to that of the integer remainder operator; this may be compared with the C library function fmod. The IEEE 754 remainder operation may be computed by the library routine Math.IEEEremainder.

The result of a floating-point remainder operation is determined by the rules of IEEE 754 arithmetic:

If either operand is NaN, the result is NaN.
If the result is not NaN, the sign of the result equals the sign of the dividend.
If the dividend is an infinity, or the divisor is a zero, or both, the result is NaN.
If the dividend is finite and the divisor is an infinity, the result equals the dividend.
If the dividend is a zero and the divisor is finite, the result equals the dividend.
In the remaining cases, where neither an infinity, nor a zero, nor NaN is involved, the floating-point remainder r from the division of a dividend n by a divisor d is defined by the mathematical relation r = n - (d ⋅ q) where q is an integer that is negative only if n/d is negative and positive only if n/d is positive, and whose magnitude is as large as possible without exceeding the magnitude of the true mathematical quotient of n and d.
Evaluation of a floating-point remainder operator % never throws a run-time exception, even if the right-hand operand is zero. Overflow, underflow, or loss of precision cannot occur.

java 求餘操作骨灰級

學到這裡,或許有人沾沾自喜,我都掌握了求餘的所有規則,看來需要給你潑潑冷水:

    public static void main(String[] args) {
        final int MODULUS = 3;
        int[] histogram = new int[MODULUS];
        // Iterate over all ints (Idiom from Puzzle 26)
        int i = Integer.MIN_VALUE;
        do {
        histogram[Math.abs(i) % MODULUS]++;
        } while (i++ != Integer.MAX_VALUE);
        for (int j = 0; j < MODULUS; j++)
        System.out.println(histogram[j] + " ");
    }

這個程式會列印什麼?有人經過繁瑣複雜的算出一個結果:

1431655765 1431655766 1431655765

但其實,上述程式執行報錯:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -2
    at com.java.puzzlers.ModTest.main(ModTest.java:11)

 

為什麼陣列會出現索引 -2?奇怪吧?要回答這個問題,我們必須要去看看Math.abs 的文件

 /**
 * Returns the absolute value of an {@code int} value.
 * If the argument is not negative, the argument is returned.
 * If the argument is negative, the negation of the argument is returned.
 *
 * <p>Note that if the argument is equal to the value of
 * {@link Integer#MIN_VALUE}, the most negative representable
 * {@code int} value, the result is that same value, which is
 * negative.
 *
 * @param a the argument whose absolute value is to be determined
 * @return the absolute value of the argument.
 */
 public static int abs(int a) {
 return (a < 0) ? -a : a;
 }
特意說明,如果是Integer#MIN_VALUE,返回負數

java裡有很多小技巧,需要我們勤翻api和jsl,多學習多練習。

參考資料:

【1】https://baike.baidu.com/item/%E5%B9%B4%E5%B9%B4%E6%9C%89%E4%BD%99/7625174?fr=aladdin

【2】https://docs.oracle.com/javase/specs/jls/se12/html/jls-15.html#jls-15.17.3

【3】java