写之前,先来看看标准C?
其实,很多时候我们都会混淆一个概念:标准C? 这里简要分享下,标准C其实就是ANSI C标准,里面收纳了绝大部分函数,但是人们发现有些经常使用的API没有被收纳,比如今天的主题–小数转字符串,所以这一部分函数叫做非标准C, 实际上:标准C和非标准C同等重要,现在也被大多数编译厂商收录,也就是现在编译器(既有标准C,也有非标准C),导致很多人不区分它们,成为C库。不过遗憾的是,标准委员会从C99就没有再更新了,所以对于早期的编译器可能不识别非标准C函数。
下面是C99的标准C的stdlib.h的API,可以和你手中的对比下,你会发现你的库会多几个API。话句话说C程序设计语言(第2版•新版) 徐宝文里面提到的是标准C。 - #ifdef __EDG_RUNTIME_USES_NAMESPACES
- #ifndef __STDLIB_NO_EXPORTS
- #ifndef __STRICT_ANSI__
- using std::atoll;
- using std::strtoll;
- using std::strtoull;
- using std::lldiv_t;
- #endif
- using std::div_t;
- using std::ldiv_t;
- using std::atof;
- using std::atoi;
- using std::atol;
- using std::strtod;
- using std::strtol;
- using std::strtoul;
- using std::rand;
- using std::srand;
- using std::calloc;
- using std::free;
- using std::malloc;
- using std::realloc;
- using std::__heapprt;
- using std::__heapstats;
- using std::__heapvalid;
- using std::abort;
- using std::atexit;
- using std::exit;
- using std::getenv;
- using std::system;
- using std::bsearch;
- using std::qsort;
- using std::abs;
- using std::div;
- using std::labs;
- using std::ldiv;
- #ifndef __STRICT_ANSI__
- using std::llabs;
- using std::lldiv;
- #endif
- using std::__sdiv32by16;
- using std::__udiv32by16;
- using std::__sdiv64by32;
- using std::__rt_sdiv32by16;
- using std::__rt_udiv32by16;
- using std::__rt_sdiv64by32;
- using std::__fp_status;
- using std::mblen;
- using std::mbtowc;
- using std::wctomb;
- using std::mbstowcs;
- using std::wcstombs;
- using std::__use_realtime_heap;
- using std::__use_two_region_memory;
- using std::__C_library_version_string;
- using std::__C_library_version_number;
- using std::size_t;
- #endif
- #endif
复制代码
这里分享一个我常用的网站,可以方便的查找相关函数 (stdlib.h) http://www.cplusplus.com/reference/cstdlib/
1 小数转字符串–标准C
事实上,标准C语言没有提供这个函数,当然非标准C提供了,如果使用的C编译器比较老,无法使用非标准C的API,不过有意思的是可以绕个弯完成。 使用sprintf,其中s表示string–字符串。专门处理字符串的,也就是字符数组、字符指针的。如果前缀是f表示file–专门处理文件的。 - int sprintf ( char * str, const char * format, ... );
复制代码
使用如下: - Vol = 0.0123456;
- uint8 bufData[16] = {0};
- sprintf(bufData, "%0.10f", Vol);
复制代码
这也完成了从小数转成字符串的功能了 甚至,还可以像printf那样,加一些描述,如 - sprintf(bufData, "Voltage%0.10f\n", Vol);
复制代码
2 小数转字符串–非标准C
头文件
非标准C提供了好几个函数,如fcvt、ecvt 、gcvt,其实是一族cvt函数,有好几个,感兴趣可以看看 - #include <stdlib.h>
- char *ecvt (double Value, int NumberOfDigits, int* DecimalPointer, int* Sign);
- char *fcvt (double Value, int NumberOfDigits, int* DecimalPointer, int* Sign);
- char *gcvt (double Value, int NumberOfDigits, char* Buffer);
复制代码
我觉得gcvt最好用了,因为它就是小数转字符串,结果存在Buffer中,下面讲讲gcvt - char *gcvt (double Value, int NumberOfDigits, char* Buffer);
- Value: 输入整数
- NumberOfDigits: 有效位数,不包括正负号和小数点
- Buffer: 目标结果
复制代码
一个案例 - double f = -1000.0045673;
- char buf[16] = {0};
- gcvt(f, 7, buf);
- printf("string= %s\n", buf);
复制代码
输出:
之后分享下ecvt函数,会自动去掉小数点和正负号 - char *ecvt (double Value, int NumberOfDigits, int* DecimalPointer, int* Sign);
- Value: 输入整数
- NumberOfDigits: 有效位数,不包括正负号和小数点
- DecimalPointer: 小数点的位置,
- Sign: 负数为1,整数为0
复制代码
举个例子
小数 | DecimalPointer | Sign |
---|
0.12 | 0 | 0 | -0.12 | 0 | 1 | -0.0012 | -2 | 1 | 12.34 | 2 | 0 |
总结:DecimalPointer含义 - 1. 正数, 小数点的位置,如上12.34小数点在位置2处
- 2. 0, 0.1到1的小数
- 3. 负数, 就是比0.1更小的啦,其绝对值刚好是小数点后零的个数(到第一个非零),如0.0012,小数点后有两个零才到1
复制代码
看个案例 - int main(void) {
- double val = -0.0012;
- int point, sign; /**<point是小数点的位置,sign=1为负数且point<0 */
- char *str = ecvt(val, 16, &point, &sign);
- printf("str= %s\tpoint=%d\tsign=%d\n", str, point, sign);
- return 0;
- }
复制代码
输出 - str= 1200000000000000 point=-2 sign=1
复制代码
总结:
- 函数不会添加小数点和正负号
- NumberOfDigits是字符串的长度
- DecimalPointer是小数点的位置
- Sign指示正负数
最后,附上一个自定义的小数转字符串的函数,其中也是调用了ecvt,其实没啥用,不过里面处理了很多情况 - void doubkeToString(double val, char *buf) {
- int i = 0; /**<目标buf下标 */
- int j = 0; /**<源下标str下标 */
- int point, sign; /**<point是小数点的位置,sign=1为负数且point<0 */
- char *str = ecvt(val, 16, &point, &sign);
-
- if (point <= 0) { /**<小于1的数*/
- /**<为负数,需要加上负号 */
- if (sign == 0){
- buf[i++] = '0';
- buf[i++] = '.';
- } else {
- buf[i++] = '-';
- buf[i++] = '0';
- buf[i++] = '.';
- }
- point = -point;
- while (point--) /**<0.0000xxx模式 */
- buf[i++] = '0';
- while (i < 15 && str[j]!='0') /**<拷贝剩余数据,去除后面的0 */
- buf[i++] = str[j++];
- } else { /**<大于1的数*/
- if (sign == 1) {
- buf[i++] = '-';
- }
- while (j < point) /**<开始copy */
- buf[i++] = str[j++];
- buf[i++]= '.'; /**<添加小数点 */
- int pointI = i;
- int isAllZero = 1;
- while (i<15 && (isAllZero || str[j]!='0')) { /**<拷贝剩余位数,去除后面的0,前面的0要保留,-100.0001000 */
- if (str[j]!='0')
- isAllZero = 0;
- buf[i++] = str[j++];
- }
- if (isAllZero) { /**<100.0000000 */
- buf[pointI+1] = '\0';
- }
- }
- }
复制代码 |