我一直在嘗試為 Motorola 68000(原始的,沒有浮點協處理器)創建一個庫,可以將binary32 浮點值轉換為可以在螢屏上顯示的文本。我正在使用這個將浮點數轉換為十六進制的網站來分析邏輯。這是我在該網站上的輸出,測驗值為 0x3E400000:
0x3e400000
3 e 4 0 0 0 0 0
0011 1110 0100 0000 0000 0000 0000 0000
0 01111100 10000000000000000000000
sign exponent mantissa
1 124 1.10000000000000000000000 (binary)
1 * 2^(124 - 127) * 1.5
1 * 0.125000000 * 1.5
問題是,這一切似乎都是回圈邏輯。如果沒有浮點協處理器,您將如何計算和存盤 2^-3 或 1.5?不是每個都需要通過與您的輸入相同的例程運行嗎?等等?
如果有幫助,這就是我的匯編代碼中的內容。如果有你不認識的指令,我已經創建了宏來幫助我,我已經在評論中解釋了它們。也許這會讓我想做的事情更清楚。
Float2Text:
;input: d0
;clobbers d1
pushRegs d2-d7 ;MOVEM.L D2-D7,-(SP)
MOVEQ.L #0,D7
MOVEQ.L #0,D6
MOVEQ.L #0,D5
MOVEQ.L #0,D4
CLX ;ANDI # 001111,CCR
MOVE.L D0,D2
ROXL.L D0
ROXL.L D7 ;TRANSFER SIGN BIT INTO D7
SWAP D0
ROR.W #8,D0 ;GET EXPONENT INTO BOTTOM BYTE
CMP.B #0,D0
BEQ .isZero
CMP.B #111111,D0
BEQ .isInfinity
SUB.B #127,D0
;THIS IS THE UNBIASED EXPONENT OF 2
;NOW WE HAVE THE EXPONENT IN D0, AND THE SIGN BIT IN D7
;WE NEED TO SUM UP THE MANTISSA BITS.
MOVE.B D0,D3
MOVE.L D2,D0
AND.L # 000000011111111111111111111111,D0 ;CLEAR SIGN AND EXPONENT
.isZero:
.isInfinity:
.done
popRegs d2-d7 ;MOVEM.L (SP) ,D2-D7
RTS
uj5u.com熱心網友回復:
對于 Float2Text:
浮點數更準確地描述為“base 2 floating point”。例如,二進制數 1.25 表示為 1.01b 或 101b >> 2。
您最初的目標應該是將“base 2 floating point”轉換為“base 10 floating point”。例如,對于“以 10 為底的浮點”,數字 1.25 將是指數為 -2 的數字(字符?) 125,例如“125 * 10^(-2)”。一旦你有了“base 10 floating point”中的數字,它就變得相對容易列印(例如,只需在正確的位置插入小數點時列印數字)。
首先將“以 2 為底的浮點”數拆分為有符號整數“以 2 為底的尾數”(同時注意“隱含的前導 1”,如果它不是次范數)和一個有符號整數“以 2 為底的指數”(同時采用照顧偏見)。
下一個; 如果“以 2 為底的指數”為負數,則將原始數字乘以 10,同時減少“以 10 為底的指數”以保持跟蹤。請注意,您可以通過將“base 2 mantissa”乘以 5 并增加“base 2 exponent”來乘以 10。以同樣的方式,您可以通過將“base 2 mantissa”乘以 25 并將 2 添加到“base 2 exponent”來乘以 100;或者通過將“base 2 mantissa”乘以 125 并將 3 添加到“base 2 exponent”等乘以 1000。您可以通過使用現有的“base 2 exponent”選擇正確的乘數來加快速度;例如,可能有點像:
while(base2_exponent < 0) {
switch(base2_exponent) {
case -1:
base2_mantissa *= 5; base2_exponent -= 1;
base10_exponent = 1;
break;
case -2:
base2_mantissa *= 25; base2_exponent -= 2;
base10_exponent = 2;
break;
case -3:
base2_mantissa *= 125; base2_exponent -= 3;
base10_exponent = 3;
break;
default:
base2_mantissa *= 625; base2_exponent -= 4;
base10_exponent = 4;
break;
}
}
這將導致一個小問題 - 對于最壞的情況(非常小的數字);“base 2 mantissa”可以增長到大約 160 位。為了解決這個問題,你必須使用“大整數”(你不能只base2_mantissa *= 625;使用普通整數,需要更多類似的東西big_integer_multiply(&base2_mantissa, 625);)。
下一個; 如果“以 2 為底的指數”為正數;將“base 2 mantissa”乘以 2 并減少“base 2 exponent”(或者換句話說,將“base 2 mantissa”左移“base 2 exponent”)。這有一個類似的問題 - 對于最壞的情況(非常大的數字),“以 2 為底的尾數”可以增長到大約 160 位。
此時,“base 2 exponent”將為零,可以忽略。
下一步是將“以 2 為底的尾數”轉換為“以 10 為底的尾數”。從理論上講,這不應該那么難 - 可能有點像while(base2_mantissa > 0) { *dest = base2_mantissa % 10 '0'; base2_mantissa /= 10; dest--; },除了你將使用“大整數”(160位整數)進行模數和除法。然而; 最好從最高有效數字到最低有效數字。要做到這一點,有一個 10 次方的查找表(例如 1、10、100、1000、10000 ......),找到不大于尾數的最大除數,然后做一些模糊的事情,比如while(base2_mantissa > 0) { divisor = find_largest_divisor_from_table(base2_mantissa); *dest = base2_mantissa / divisor '0'; base2_mantissa %= divisor; dest ; }. 這允許您提前退出 - 例如,如果您最多只需要 10 位有效數字,那么您可以使用 10 的固定大小緩沖區char并在您有 10 位數字時停止。當然,所有這些(除數表等)也需要使用“大整數”。
最后一步是列印“以 10 為底的尾數”,同時確保在“以 10 為底的指數”表示的位置插入小數點字符 ('.')。如果您在上一步中使用了“最高有效位到最低有效位”,那么這可以與上一步結合使用(例如,一次列印一個數字而不存盤“base 10 mastissa”,同時在正確的地方)。
對于其他浮點運算:
否定是微不足道的(符號位的 XOR)。
對于其他一切,分 3 個階段進行 - 將源值拆分為“有符號整數尾數”和“有符號整數指數”(主要如上所述);做一個“中間階段”,這取決于操作是什么;然后將得到的“有符號整數尾數”和“有符號整數指數”組合回正確的格式。
作為一般指南(沒有復雜性 - 上溢、下溢、舍入、NaN 等),“中間階段”是:
乘法:result_mantissa = src1_mantissa * src2_mantissa;result_exponent = src1_exponent src2_exponent - 尾數大小;
除法:result_mantissa = (src1_mantissa << mantissa_size) / src2_mantissa; result_exponent = src1_exponent - src2_exponent;
加法:找到具有最大正指數的值;將另一個值的尾數右移,同時增加其指數,直到指數匹配;結果_mantissa = src1_mantissa src2_mantissa;結果_exponent = src1_exponent;
減法:否定第二個值并改用加法
左移:如果尾數太小(低于正常值),盡可能多地左移(同時確保它不會變得太大);然后將剩余的移位計數添加到指數。
對于右移:盡可能從指數中減去移位計數,同時保持指數不會變得“太負”。如果有任何左移計數,則將尾數右移(形成次正常)。
請注意,將生成的“有符號整數尾數”和“有符號整數指數”組合回正確的格式應該包括某種while(result_mantissa's magnitude is too big) { result_mantissa >> 1; result_exponent ); }; 最終有點混亂(可以/應該考慮舍入模式)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/489742.html
