終於來到 10 了, 這是目前最長的系列, 真是長壽。浮點數原來是這麼麻煩的東西, 這篇並不是要介紹浮點數的格式、原理, 而是怎麼把浮點數印出來。
printf("%f") 就搞定了, 你這麼說: 不過標題是
作業系統之前的程式, 所以我們得自己提供這個功能, 當然不用把 printf 做出來, 先把 float 轉成 C 字串就可以了。
先來個提外話:
我在 p103 模擬器上測試程式碼時, 使用不同版本的 toolchain, 得到了以下的錯誤訊息, p103 的範例有很多是我不能掌握的程式碼, 單純是為了方便而使用 p103 模擬器。
若你遇到這問題, 請參考
Fix “registers may not be the same” ARM GCC error
大概是這樣修正, 我不知道為什麼用了不同的 toolchain 會這樣, 至於 & 是表示被修飾的操作符只能作為輸出。
回到浮點數。
ftoa.cpp 從
How to implement “ char * ftoa(float num) ” without sprintf() library function in C, C++ and JAVA? 抄來的, 我做了一些修改, 避免使用 math.h 的東西, 拿掉 log10 和 pow 這兩個 function, 作業系統之前的程式都要自己來, 儘量不要用現成的 library; 作業系統之下的版本當然就直接 call log10 和 pow。這是面試題目, 多了解不會吃虧的。
這個演算法的重點有兩個:
- c 語言型別轉換知識 (這你不可能想得到, 只有靠閱讀 c 語言資料才會知道)
- 演算法 (你可以想出來怎麼做)
c 怎麼把浮點數轉成整數?
How do I round numbers?
這是知識。
怎麼把數字 123 轉成 c 字串的 123, 這是演算法, 缺一不可。程式員真辛苦, 除了讀書還要思考, 可不是嘴炮般的說些自己想像的東西。
int i = 1.35f; 浮點數 1.35 會轉成整數 1 存給 i, 再用 1.35 - 1 得到 0.35, 有了 1 和 0.35, 想辦法把他們變成 char 存到 c 字串就搞定。
請參考 float.cpp L242 把整數 1 轉成字串, L207 把 0.35 轉成字元 3, 5, 最後在他們中間加上 "." 就搞定了。
int main(void)
{
init_rs232();
USART_Cmd(USART2, ENABLE);
float f=1.5;
myprint_float(f);
myprint("\r\nabc");
while(1);
}
還沒完, 怎麼 compile 浮點運算的程式碼可是個問題, 目標放在使用軟體的
浮點運算函式庫 - libgcc, 所以要加上 -mfloat-abi=soft, link 時需要加上 -lgcc (arm-none-eabi-gcc -print-libgcc-file-name 可以顯示 libgcc 的路徑), 才剛說了大話馬上被打臉, 我還是用上了 gcc 裡頭的浮點運算函式庫。
我不是想偷懶, 只是神功還沒練成, 秘笈在這:
如果你遇上這些連結錯誤的話, 可能是忘了加上 -lgcc, 很奇怪, 有的 toolchain 不需要明確指出 -lgcc, 有的卻需要, libgcc 明明是 gcc 內建的, 怎麼會需要特別指定呢? 奇怪。這是我在 p103 模擬器上的測試程式碼, p103 的範例有很多是我不能掌握的 code, 是我純粹為了方便測試為主。你可能發現前面講過了, 因為是從這裡複製貼上修改到前面的。
正確印出 float 1.5, 別看我寫的很輕鬆, 期間可是克服不少問題才把這 1.5 辛苦秀出來。
我當然不會滿足模擬器上的測試, 以下是 stm32discovery 完整範例:
https://github.com/descent/stm32f4_prog/tree/master/float
我有 gcc 4.7, 4.8, 4.9 三個版本的 toolchain, 測試結果有所不同, stm32discovery 的版本我在 gcc 4.91 測試成功, 其他的都不正常。
debian 提供的 arm-none toolchain 有支援 mulit-lib, 什麼是 multi-lib, 很難解釋, 直接看:
debian64:~# dpkg -L libnewlib-arm-none-eabi|grep libm
/usr/lib/arm-none-eabi/newlib/libm.a
/usr/lib/arm-none-eabi/newlib/armv7-ar/thumb/libm.a
/usr/lib/arm-none-eabi/newlib/armv7-ar/thumb/fpu/libm.a
/usr/lib/arm-none-eabi/newlib/armv7-ar/thumb/softfp/libm.a
/usr/lib/arm-none-eabi/newlib/fpu/libm.a
/usr/lib/arm-none-eabi/newlib/thumb/libm.a
/usr/lib/arm-none-eabi/newlib/thumb/armv7-ar/fpu/vfpv3-d16/be/libm.a
/usr/lib/arm-none-eabi/newlib/armv7e-m/libm.a
/usr/lib/arm-none-eabi/newlib/armv7e-m/fpu/libm.a
/usr/lib/arm-none-eabi/newlib/armv7e-m/softfp/libm.a
/usr/lib/arm-none-eabi/newlib/armv6-m/libm.a
/usr/lib/arm-none-eabi/newlib/armv7-m/libm.a
debian64:~# dpkg -L libnewlib-arm-none-eabi|grep libc
/usr/lib/arm-none-eabi/newlib/armv7-ar/thumb/fpu/libc.a
/usr/lib/arm-none-eabi/newlib/armv7-ar/thumb/fpu/libc_nano.a
/usr/lib/arm-none-eabi/newlib/armv7-ar/thumb/softfp/libc.a
/usr/lib/arm-none-eabi/newlib/armv7-ar/thumb/softfp/libc_nano.a
/usr/lib/arm-none-eabi/newlib/armv7-ar/thumb/libc.a
/usr/lib/arm-none-eabi/newlib/armv7-ar/thumb/libc_nano.a
/usr/lib/arm-none-eabi/newlib/fpu/libc.a
/usr/lib/arm-none-eabi/newlib/fpu/libc_nano.a
/usr/lib/arm-none-eabi/newlib/libc.a
/usr/lib/arm-none-eabi/newlib/thumb/armv7-ar/fpu/vfpv3-d16/be/libc.a
/usr/lib/arm-none-eabi/newlib/thumb/armv7-ar/fpu/vfpv3-d16/be/libc_nano.a
/usr/lib/arm-none-eabi/newlib/thumb/libc.a
/usr/lib/arm-none-eabi/newlib/thumb/libc_nano.a
/usr/lib/arm-none-eabi/newlib/libc_nano.a
/usr/lib/arm-none-eabi/newlib/armv7e-m/fpu/libc.a
/usr/lib/arm-none-eabi/newlib/armv7e-m/fpu/libc_nano.a
/usr/lib/arm-none-eabi/newlib/armv7e-m/softfp/libc.a
/usr/lib/arm-none-eabi/newlib/armv7e-m/softfp/libc_nano.a
/usr/lib/arm-none-eabi/newlib/armv7e-m/libc.a
/usr/lib/arm-none-eabi/newlib/armv7e-m/libc_nano.a
/usr/lib/arm-none-eabi/newlib/armv6-m/libc.a
/usr/lib/arm-none-eabi/newlib/armv6-m/libc_nano.a
/usr/lib/arm-none-eabi/newlib/armv7-m/libc.a
/usr/lib/arm-none-eabi/newlib/armv7-m/libc_nano.a
同一套 toolchain 提供了不同版本的 libm.a, 這就是 multi-lib, 那要怎麼知道要 link 那一個, 施主, 程式你寫的, 自己要知道阿!
-print-multi-lib 這個參數可以顯示出什麼參數會去用那個路徑的 libm。
descent@debian64:$ arm-none-eabi-gcc -print-file-name=libm.a
/usr/lib/gcc/arm-none-eabi/4.9.3/../../../arm-none-eabi/lib/libm.a
descent@debian64:$ arm-none-eabi-gcc -march=armv6s-m -mthumb -print-file-name=libm.a
/usr/lib/gcc/arm-none-eabi/4.9.3/../../../arm-none-eabi/lib/armv6-m/libm.a
list 1. L5 的 @mthumb@march=armv6s-m, 就是指 -mthumb -march=armv6s-m, 把 @ 換成 - 就可以了。
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。