C语言中小数转字符串

发布时间 2023-12-12 10:02:59作者: 虎啸岳林

小数转字符串
写之前,先来看看标准C?
1 小数转字符串--标准C
2 小数转字符串--非标准C
写之前,先来看看标准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, ... );
1
使用如下:

Vol = 0.0123456;
uint8 bufData[16] = {0};
sprintf(bufData, "%0.10f", Vol);
1
2
3
这也完成了从小数转成字符串的功能了
甚至,还可以像printf那样,加一些描述,如

sprintf(bufData, "Voltage%0.10f\n", Vol);
1
2 小数转字符串–非标准C
头文件

/usr/include/stdlib.h
1
非标准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);
1
2
3
4
我觉得gcvt最好用了,因为它就是小数转字符串,结果存在Buffer中,下面讲讲gcvt

char *gcvt (double Value, int NumberOfDigits, char* Buffer);
Value: 输入整数
NumberOfDigits: 有效位数,不包括正负号和小数点
Buffer: 目标结果
1
2
3
4
一个案例

double f = -1000.0045673;
char buf[16] = {0};
gcvt(f, 7, buf);
printf("string= %s\n", buf);
1
2
3
4
输出:

string= -1000.005
1
之后分享下ecvt函数,会自动去掉小数点和正负号

char *ecvt (double Value, int NumberOfDigits, int* DecimalPointer, int* Sign);
Value: 输入整数
NumberOfDigits: 有效位数,不包括正负号和小数点
DecimalPointer: 小数点的位置,
Sign: 负数为1,整数为0
1
2
3
4
5
举个例子

小数 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
1
2
3
看个案例

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
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';
}
}
}

原文链接:https://blog.csdn.net/weixin_39956356/article/details/115312758