又來到了明天還要上班的週日

我難過(´・_・`)

算了反正下禮拜也還有週末,不怕不怕

今天要來的介紹是進制轉換

由於我的公司是硬體公司,而我工作內容基本都是撰寫控制硬體設備的輔助工具

因此時常要寫一些跟資訊交換有關的程式

而在這些程式中,通訊的方式與協定就顯得無比重要

又因為我們公司的設備溝通都以16進制(Hex)為主

因此才又回顧那些在數位邏輯課中所教到的進制轉換問題(´・з・)

 

今天的程式很少很簡單,但是背後都牽涉到那些當年早就還給老師的數位邏輯的問題

我在這邊懶得講理論,網路上隨便打都有,建議不會的同學可以先爬文喔~

當然真的搞不懂也歡迎發問XD

那今天的介紹開始


在開始之前,請先上這個網站

https://cryptii.com/pipes/integer-encoder

這是一個我目前覺得最好用的位數計算器(`・ω・´)

而今天我APP計算出來的結果基本上也是用這個下去比對做解答

那麼請你按照我這樣設定

完成後可以自己試著輸入一些16進制的數值

了解操作後,接著正式開始我們今天的介紹

成品:

 

今天預計要達成的目標:

1.輸入16進制

2.算出10進制值

3.算出8進制值

4.算出2進制值

5.算出帶負號的值

 

最後,Github在此

https://github.com/thumbb13555/ConvertValueExample


進制問題其實說容易

因為其實在Java中,他老早就幫你寫好了

你只要懂得運用即可

在型態轉換的工具中,有一個轉換工具是String轉int的

如下:

int i = Integer.parseInt("99887766");

"99887766"雖然是一串字串格式,但是用這個工具就可以轉為int格式

此外還有很多,像是轉double、char或其他轉為String等等都很常見

關於這方面可參考

https://hsinichi.pixnet.net/blog/post/5317015

他寫得很詳細。

那重點就在這裡

像我在收到這些資訊時,因為只有String格式可以自由拆解資料

因此通常我拿到資料後需要拆解的我都會先轉String格式

但問題來了,如果你今天輸入了16進制的A~F

你就會出現以下問題

java.lang.NumberFormatException: For input string: "a2"
        at java.lang.Integer.parseInt(Integer.java:615)
        at java.lang.Integer.parseInt(Integer.java:650)
        at com.example.convertvalueexample.MainActivity.convert(MainActivity.java:32)
        at com.example.convertvalueexample.MainActivity.lambda$onCreate$0$MainActivity(MainActivity.java:26)
        at com.example.convertvalueexample.-$$Lambda$MainActivity$nuSFrx0u7jA69cc4gyoNVvsrmPM.onClick(Unknown Source:2)
        at android.view.View.performClick(View.java:7339)
        at android.widget.TextView.performClick(TextView.java:14222)
        at android.view.View.performClickInternal(View.java:7305)
        at android.view.View.access$3200(View.java:846)
        at android.view.View$PerformClick.run(View.java:27787)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7078)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)

 

這個摁提的意思就是他預設轉型為10進制,但是你輸入了a2

因此拋出NumberFormatException

這時候很簡單,只要把程式改成

 

int i = Integer.parseInt("22FA",16);

就可以解決問題囉(`・ω・´)

而你接到的數字也將會是10進制

再來是轉出問題

轉出也很簡單

只要

Integer.toHexString(123);

就可以囉!

其他的就只要這樣

Integer.toBinaryString(123);
Integer.toOctalString(123);

一樣可以得到相同的效果

也就是說,其實程式裡面

只要有上述的轉入及轉出,就可大體完成整個功能了

結果大概像這樣

然後...

恩?好像哪裡怪怪的

這時用我前面的連結來驗證一下

答案其實就是,我一開始請你選擇的是16位元簽署 (16-bit signed Integer)

因此如果你選擇16-bit unsigned Integer

得到的就是剛才的結果一樣

但問題是...很多時候我們要的就是那些負號RRRRR(ノ`□´)ノ⌒┻━┻

這時候我們第一目標先處理好轉10進制的問題

在我範例程式中的第32行

long input = (short)Integer.parseInt(edInput.getText().toString(),16);

就結論而言

只要加入這行就可以搞定16進制的部分了

至於原理在這

https://stackoverflow.com/questions/15202958/16-bit-hex-string-to-signed-int-in-java

其中的這

在下英文不好就不幫忙翻譯了~

簡單來說就是因為強制轉型短整數的緣故,而忽略第16位元

取而代之使用地15位元作為符號位元(判定正負)

因此才能有效顯示

 

再來就是八進制與二進制的問題啦

關於八進制轉負數的問題,可能因為八進制相較二進與十六進算比較少用

因此網路上關於如何取負數的資料幾乎沒有

這時候你就要想想你當年的數位邏輯老師的臉龐了

敢情大概也想不起來吧(・ω・`)………..

 

其實這時候就是"補數"的概念要出場啦(`・ω・´)b

關於這部分可參考

https://www.javaworld.com.tw/jute/post/view?bid=29&id=246959

其中你只要在你的數值前面加入“~”就好

詳情Github中35行

講講2進位的補數

如果你今天數值是0100 1101

那取補數就會變1011 0010

這時候補數還沒完,你還要再+1才有用

因此就變成1011 0011

最後手動加上負號就完成

因此最後出來的程式長這樣

edBinary.setText("-"+Long.toBinaryString(~input+1));

Long是長整數,改為int也可以

至於八進位的原理也是一樣,一樣的程式套用就好

最後結果

 

最後我們在回頭看程式

public class MainActivity extends AppCompatActivity {
    String TAG = MainActivity.class.getSimpleName()+"m";
    Button btConvert;
    EditText edInput,edBinary,edOctal,edDecimal;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btConvert   = findViewById(R.id.button);
        edInput     = findViewById(R.id.input);
        edBinary    = findViewById(R.id.binary);
        edOctal     = findViewById(R.id.octal);
        edDecimal   = findViewById(R.id.decimal);
        btConvert.setOnClickListener((v)->{convert();});

    }

    private void convert(){
            if (edInput.getText().toString().length()>0){
                int input = (short)Integer.parseInt(edInput.getText().toString(),16);
                edDecimal.setText(String.valueOf(input));
                if(input<0){
                    edBinary.setText("-"+(Long.toBinaryString(~input+1)));
                    edOctal.setText("-"+Long.toOctalString(~input+1));
                }else{
                    edBinary.setText(Long.toBinaryString(input));
                    edOctal.setText(Long.toOctalString(input));
                }

                if (Long.toBinaryString(input).length()<8){
                    edBinary.setText(String.format("%08d",Integer.parseInt(Long.toBinaryString(input))));
                }
            }
    }
}

醬子好像多少有看懂了一些嗎XD

喔對了補充

因為本身在補數判定只能判定4個字元

因此我在輸入筐內有設定這個

android:maxLength="4"

限制最大輸入為4這樣,特請多多留意囉

還有最後藍字的部分是針對二進位如果少於8格字元就自動補0的方法

參考看看吧!

arrow
arrow
    創作者介紹
    創作者 碼農日常 的頭像
    碼農日常

    碼農日常大小事

    碼農日常 發表在 痞客邦 留言(1) 人氣()