作業

haoyinuo發表於2024-09-28

原碼、反碼和補碼

原碼

原碼是最直觀的表示方法,用第一位表示符號(0為正,1為負),其餘位表示數值本身。
例如:
+5 的原碼是 0101(假設我們使用4位二進位制數)
-5 的原碼是 1101

反碼

反碼用於簡化計算機中的減法操作。對於正數的反碼與其原碼相同;對於負數,其反碼是將原碼中除符號位外的所有位取反(0變1,1變0)。
例如:
+5 的反碼是 0101
-5 的反碼是 1010

補碼

補碼也是用於簡化計算機中的減法操作。正數的補碼與其原碼相同;負數的補碼是其反碼加1。
例如:
+5 的補碼是 0101
-5 的補碼是 1011

Java中的表示

Java中的整數預設是採用補碼形式表示的。

示例程式

以下是一個Java程式,它將執行位操作並顯示結果,我們可以用這些結果來驗證Java中整數的表示方式。

public class BitwiseOperations {
    public static void main(String[] args) {
        int positiveNumber = 5;
        int negativeNumber = -5;

        // 輸出原碼、反碼和補碼
        System.out.println("正數 +5 的二進位制表示(補碼形式,Java中的實際表示): " + Integer.toBinaryString(positiveNumber));
        System.out.println("負數 -5 的二進位制表示(補碼形式,Java中的實際表示): " + Integer.toBinaryString(negativeNumber));

        // 手動計算反碼和補碼進行驗證
        String positiveBinary = Integer.toBinaryString(positiveNumber);
        String negativeBinary = Integer.toBinaryString(negativeNumber);

        // 正數的原碼、反碼、補碼相同
        System.out.println("正數 +5 的原碼: " + positiveBinary);
        System.out.println("正數 +5 的反碼: " + positiveBinary);
        System.out.println("正數 +5 的補碼: " + positiveBinary);

        // 負數的原碼
        String negativeOriginal = "1" + positiveBinary.substring(1);
        System.out.println("負數 -5 的原碼: " + negativeOriginal);

        // 負數的反碼(除符號位外取反)
        String negativeOnesComplement = negativeOriginal.chars()
                .mapToObj(c -> c == '0' ? "1" : "0")
                .collect(Collectors.joining());
        System.out.println("負數 -5 的反碼: " + negativeOnesComplement);

        // 負數的補碼(反碼+1)
        int negativeTwosComplement = Integer.parseInt(negativeOnesComplement, 2) + 1;
        System.out.println("負數 -5 的補碼: " + Integer.toBinaryString(negativeTwosComplement));

        // 驗證Java中的表示
        System.out.println("Java中的負數 -5 補碼錶示(預期與上面計算得到的補碼相同): " + negativeBinary);
    }
}

手工計算與程式輸出比對

當你執行上述程式時,觀察到輸出結果,並將其與手工計算的結果進行比對。對於正數,原碼、反碼、補碼都是相同的,而對於負數,Java中的表示(即補碼)應該與你手工計算得到的補碼一致。
這表明Java確實使用的是補碼來表示整數。

Java中的變數具有特定的作用域

它決定了變數的可見性和生命週期。下面我將編寫一個Java示例程式,在不同的作用域中定義同名的變數,並觀察輸出的值。

public class ScopeExample {
    // 類變數
    static int variable = 10;

    public static void main(String[] args) {
        // 方法中的區域性變數,遮蔽了類變數
        int variable = 20;
        System.out.println("方法中的區域性變數: " + variable); // 輸出區域性變數的值

        // 在方法內部定義一個程式碼塊,建立一個新的作用域
        {
            // 在程式碼塊中定義一個同名的區域性變數,遮蔽了外層的區域性變數
            int variable = 30;
            System.out.println("程式碼塊中的區域性變數: " + variable); // 輸出程式碼塊中的區域性變數
        }

        // 程式碼塊結束後,這裡的變數引用的是最外層的區域性變數
        System.out.println("程式碼塊外的區域性變數: " + variable); // 輸出方法中的區域性變數

        // 使用類名呼叫類變數,避免遮蔽問題
        System.out.println("類變數: " + ScopeExample.variable); // 輸出類變數的值
    }
}

輸出結果:
方法中的區域性變數: 20
程式碼塊中的區域性變數: 30
程式碼塊外的區域性變數: 20
類變數: 10
類變數variable被初始化為10。
在main方法中,定義了一個同名的區域性變數variable,其值為20,這遮蔽了類變數。
在main方法內部的一個程式碼塊中,定義了另一個同名的區域性變數variable,其值為30,這遮蔽了外層的區域性變數。
當程式碼塊結束時,程式碼塊外的variable仍然引用的是main方法中的區域性變數,因此輸出20。
為了訪問被遮蔽的類變數,我們使用了ScopeExample.variable,輸出類變數的值,即10。
示例:

java中的型別轉換


看著這個圖,再查查Java中每個資料型別所佔的位數,和表示數值的範圍,你能得出什麼結論
可以總結出以下關於 Java 中基本資料型別的位寬、取值範圍及其轉換關係的結論:
byte:
位寬: 8 位(1 位元組)
取值範圍: -128 到 127
short:
位寬: 16 位(2 位元組)
取值範圍: -32768 到 32767
int:
位寬: 32 位(4 位元組)
取值範圍: -2147483648 到 2147483647
long:
位寬: 64 位(8 位元組)
取值範圍: -9223372036854775808 到 9223372036854775807
float:
位寬: 32 位(4 位元組)
取值範圍: 約 ±3.40282347E38F(有效數字約6~7位)
double:
位寬: 64 位(8 位元組)
取值範圍: 約 ±1.79769313486231570E308(有效數字約15位)
從圖中可以看出,不同資料型別之間的轉換關係如下:

無精度損失轉換:
byte -> short -> int -> long
char -> int -> long
這些轉換不會導致數值精度的丟失,因為目標型別的位寬至少與源型別相同或更大。
有精度損失轉換:
int -> float
long -> double
float -> double
這些轉換可能會導致數值精度的丟失,特別是當從一個整數型別轉換為浮點數型別時,可能會出現舍入誤差。

浮點數的表示方法

計算機使用 IEEE 754 標準來表示浮點數,這種表示方法包括三個部分:符號位、指數位和小數位(也稱為尾數位或有效數字位)。以下是 IEEE 754 標準的一些關鍵點:

符號位:決定數值的正負,0 表示正數,1 表示負數。
指數位:表示 2 的冪次,用於縮放尾數。
尾數位:表示實際的數字資訊,通常是 2 的冪次的分數。

精度限制

尾數位的限制:由於儲存空間的限制,double 型別通常有 52 位用於尾數(加上一個隱含的前導 1,總共 53 位精度),這意味著它不能精確表示所有的十進位制小數。例如,0.1 在二進位制中是一個無限迴圈小數,因此不能完全精確地表示為 double。
指數位的限制:指數位決定了數值的範圍,但是也有最大和最小的限制。超出這個範圍,數值就會變成無窮大或者非常接近零。

舍入誤差

當一個數不能精確表示時,必須進行舍入。IEEE 754 標準定義了幾種舍入模式,例如向最接近的值舍入、向上舍入或向下舍入。這種舍入過程會導致誤差。

運算誤差累積

在進行多次浮點運算時,舍入誤差會累積,導致最終結果與數學上的精確結果有較大偏差。
示例

public class FloatingPointPrecision {
    public static void main(String[] args) {
        double a = 0.1;
        double b = 0.2;
        double sum = a + b;
        System.out.println("The sum of 0.1 and 0.2 is: " + sum); // 輸出可能不是 0.3
    }
}

a + b 的結果可能不會是精確的 0.3,因為 0.1 和 0.2 在二進位制表示中都不是精確的,它們的和自然也不會是精確的。
在需要高精度計算的應用中,通常會使用 BigDecimal 類或其他專門的數值計算庫來避免這些問題。