[C.C++] C语言中小数转字符串?标准C?

738 0
Honkers 2025-3-27 05:39:39 来自手机 | 显示全部楼层 |阅读模式

写之前,先来看看标准C?

其实,很多时候我们都会混淆一个概念:标准C?
这里简要分享下,标准C其实就是ANSI C标准,里面收纳了绝大部分函数,但是人们发现有些经常使用的API没有被收纳,比如今天的主题–小数转字符串,所以这一部分函数叫做非标准C,
实际上:标准C和非标准C同等重要,现在也被大多数编译厂商收录,也就是现在编译器(既有标准C,也有非标准C),导致很多人不区分它们,成为C库。不过遗憾的是,标准委员会从C99就没有再更新了,所以对于早期的编译器可能不识别非标准C函数。

下面是C99的标准C的stdlib.h的API,可以和你手中的对比下,你会发现你的库会多几个API。话句话说C程序设计语言(第2版•新版) 徐宝文里面提到的是标准C。

  1. #ifdef __EDG_RUNTIME_USES_NAMESPACES
  2. #ifndef __STDLIB_NO_EXPORTS
  3. #ifndef __STRICT_ANSI__
  4. using std::atoll;
  5. using std::strtoll;
  6. using std::strtoull;
  7. using std::lldiv_t;
  8. #endif
  9. using std::div_t;
  10. using std::ldiv_t;
  11. using std::atof;
  12. using std::atoi;
  13. using std::atol;
  14. using std::strtod;
  15. using std::strtol;
  16. using std::strtoul;
  17. using std::rand;
  18. using std::srand;
  19. using std::calloc;
  20. using std::free;
  21. using std::malloc;
  22. using std::realloc;
  23. using std::__heapprt;
  24. using std::__heapstats;
  25. using std::__heapvalid;
  26. using std::abort;
  27. using std::atexit;
  28. using std::exit;
  29. using std::getenv;
  30. using std::system;
  31. using std::bsearch;
  32. using std::qsort;
  33. using std::abs;
  34. using std::div;
  35. using std::labs;
  36. using std::ldiv;
  37. #ifndef __STRICT_ANSI__
  38. using std::llabs;
  39. using std::lldiv;
  40. #endif
  41. using std::__sdiv32by16;
  42. using std::__udiv32by16;
  43. using std::__sdiv64by32;
  44. using std::__rt_sdiv32by16;
  45. using std::__rt_udiv32by16;
  46. using std::__rt_sdiv64by32;
  47. using std::__fp_status;
  48. using std::mblen;
  49. using std::mbtowc;
  50. using std::wctomb;
  51. using std::mbstowcs;
  52. using std::wcstombs;
  53. using std::__use_realtime_heap;
  54. using std::__use_two_region_memory;
  55. using std::__C_library_version_string;
  56. using std::__C_library_version_number;
  57. using std::size_t;
  58. #endif
  59. #endif
复制代码

这里分享一个我常用的网站,可以方便的查找相关函数
(stdlib.h)
http://www.cplusplus.com/reference/cstdlib/

1 小数转字符串–标准C

事实上,标准C语言没有提供这个函数,当然非标准C提供了,如果使用的C编译器比较老,无法使用非标准C的API,不过有意思的是可以绕个弯完成。
使用sprintf,其中s表示string–字符串。专门处理字符串的,也就是字符数组、字符指针的。如果前缀是f表示file–专门处理文件的。

  1. int sprintf ( char * str, const char * format, ... );
复制代码

使用如下:

  1. Vol = 0.0123456;
  2. uint8 bufData[16] = {0};
  3. sprintf(bufData, "%0.10f", Vol);
复制代码

这也完成了从小数转成字符串的功能了
甚至,还可以像printf那样,加一些描述,如

  1. sprintf(bufData, "Voltage%0.10f\n", Vol);
复制代码

2 小数转字符串–非标准C

头文件

  1. /usr/include/stdlib.h
复制代码

非标准C提供了好几个函数,如fcvt、ecvt 、gcvt,其实是一族cvt函数,有好几个,感兴趣可以看看

  1. #include <stdlib.h>
  2. char *ecvt (double Value, int NumberOfDigits, int* DecimalPointer, int* Sign);
  3. char *fcvt (double Value, int NumberOfDigits, int* DecimalPointer, int* Sign);
  4. char *gcvt (double Value, int NumberOfDigits, char* Buffer);
复制代码

我觉得gcvt最好用了,因为它就是小数转字符串,结果存在Buffer中,下面讲讲gcvt

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

一个案例

  1. double f = -1000.0045673;
  2. char buf[16] = {0};
  3. gcvt(f, 7, buf);
  4. printf("string= %s\n", buf);
复制代码

输出:

  1. string= -1000.005
复制代码

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

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

举个例子

小数DecimalPointerSign
0.1200
-0.1201
-0.0012-21
12.3420

总结:DecimalPointer含义

  1. 1. 正数, 小数点的位置,如上12.34小数点在位置2处
  2. 2. 0, 0.1到1的小数
  3. 3. 负数, 就是比0.1更小的啦,其绝对值刚好是小数点后零的个数(到第一个非零),如0.0012,小数点后有两个零才到1
复制代码

看个案例

  1. int main(void) {
  2. double val = -0.0012;
  3. int point, sign; /**<point是小数点的位置,sign=1为负数且point<0 */
  4. char *str = ecvt(val, 16, &point, &sign);
  5. printf("str= %s\tpoint=%d\tsign=%d\n", str, point, sign);
  6. return 0;
  7. }
复制代码

输出

  1. str= 1200000000000000 point=-2 sign=1
复制代码

总结:

  1. 函数不会添加小数点和正负号
  2. NumberOfDigits是字符串的长度
  3. DecimalPointer是小数点的位置
  4. Sign指示正负数

最后,附上一个自定义的小数转字符串的函数,其中也是调用了ecvt,其实没啥用,不过里面处理了很多情况

  1. void doubkeToString(double val, char *buf) {
  2. int i = 0; /**<目标buf下标 */
  3. int j = 0; /**<源下标str下标 */
  4. int point, sign; /**<point是小数点的位置,sign=1为负数且point<0 */
  5. char *str = ecvt(val, 16, &point, &sign);
  6. if (point <= 0) { /**<小于1的数*/
  7. /**<为负数,需要加上负号 */
  8. if (sign == 0){
  9. buf[i++] = '0';
  10. buf[i++] = '.';
  11. } else {
  12. buf[i++] = '-';
  13. buf[i++] = '0';
  14. buf[i++] = '.';
  15. }
  16. point = -point;
  17. while (point--) /**<0.0000xxx模式 */
  18. buf[i++] = '0';
  19. while (i < 15 && str[j]!='0') /**<拷贝剩余数据,去除后面的0 */
  20. buf[i++] = str[j++];
  21. } else { /**<大于1的数*/
  22. if (sign == 1) {
  23. buf[i++] = '-';
  24. }
  25. while (j < point) /**<开始copy */
  26. buf[i++] = str[j++];
  27. buf[i++]= '.'; /**<添加小数点 */
  28. int pointI = i;
  29. int isAllZero = 1;
  30. while (i<15 && (isAllZero || str[j]!='0')) { /**<拷贝剩余位数,去除后面的0,前面的0要保留,-100.0001000 */
  31. if (str[j]!='0')
  32. isAllZero = 0;
  33. buf[i++] = str[j++];
  34. }
  35. if (isAllZero) { /**<100.0000000 */
  36. buf[pointI+1] = '\0';
  37. }
  38. }
  39. }
复制代码
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

荣誉红客

关注
  • 4010
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行