摘要
本文讲述1亿以内的double类型转为中文字符串示例,该示例难点在于double类型精度处理。
代码
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <math.h>
#define INTEGER_UINT_LEN 5 //1亿以内:万/千/百/十/个
#define DECIMAL_UINT_LEN 2 // 2位小数
#define PER_UINT_SIZE 3 // 单个汉子长度
static inline int IntegerToText(unsigned int u32Value, char *szText) {
char szValue[128] = {0};
unsigned short u16Idx = 0;
unsigned int u32Offset = 0;
char *szNumStr[10] = { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
if (u32Value >= 10000) {
printf("OverMaxUnitLength:xxxx\n");
return -1;
}
while (u32Value > 0) {
szValue[u16Idx++] = u32Value % 10;
u32Value = u32Value / 10;
}
if (u16Idx == 4) { // XXXX
// X---
memcpy((szText + u32Offset), szNumStr[szValue[u16Idx - 1]], PER_UINT_SIZE);
u32Offset += PER_UINT_SIZE;
sprintf(&szText[u32Offset], "%s", "千");
u32Offset += PER_UINT_SIZE;
// X--
if (szValue[u16Idx - 2]) {
memcpy((szText + u32Offset), szNumStr[szValue[u16Idx - 2]], PER_UINT_SIZE);
u32Offset += PER_UINT_SIZE;
sprintf(&szText[u32Offset], "%s", "百");
u32Offset += PER_UINT_SIZE;
} else {
sprintf(&szText[u32Offset], "%s", "零");
u32Offset += PER_UINT_SIZE;
}
// X-
if (szValue[u16Idx - 3]) {
memcpy((szText + u32Offset), szNumStr[szValue[u16Idx - 3]], PER_UINT_SIZE);
u32Offset += PER_UINT_SIZE;
sprintf(&szText[u32Offset], "%s", "十");
u32Offset += PER_UINT_SIZE;
} else {
sprintf(&szText[u32Offset], "%s", "零");
u32Offset += PER_UINT_SIZE;
}
// X
if (szValue[u16Idx - 4]) {
memcpy((szText + u32Offset), szNumStr[szValue[u16Idx - 4]], PER_UINT_SIZE);
u32Offset += PER_UINT_SIZE;
}
} else if (u16Idx == 3) { // XXX
// X--
memcpy((szText + u32Offset), szNumStr[szValue[u16Idx - 1]], PER_UINT_SIZE);
u32Offset += PER_UINT_SIZE;
sprintf(&szText[u32Offset], "%s", "百");
u32Offset += PER_UINT_SIZE;
// X-
if (szValue[u16Idx - 2]) {
memcpy((szText + u32Offset), szNumStr[szValue[u16Idx - 2]], PER_UINT_SIZE);
u32Offset += PER_UINT_SIZE;
sprintf(&szText[u32Offset], "%s", "十");
u32Offset += PER_UINT_SIZE;
} else {
sprintf(&szText[u32Offset], "%s", "零");
u32Offset += PER_UINT_SIZE;
}
// X
if (szValue[u16Idx - 3]) {
memcpy((szText + u32Offset), szNumStr[szValue[u16Idx - 3]], PER_UINT_SIZE);
u32Offset += PER_UINT_SIZE;
}
} else if (u16Idx == 2) { // XX
// X-
memcpy((szText + u32Offset), szNumStr[szValue[u16Idx - 1]], PER_UINT_SIZE);
u32Offset += PER_UINT_SIZE;
sprintf(&szText[u32Offset], "%s", "十");
u32Offset += PER_UINT_SIZE;
// X
if (szValue[u16Idx - 2]) {
memcpy((szText + u32Offset), szNumStr[szValue[u16Idx - 2]], PER_UINT_SIZE);
u32Offset += PER_UINT_SIZE;
}
} else if (u16Idx == 1) { // X
memcpy((szText + u32Offset), szNumStr[szValue[u16Idx - 1]], PER_UINT_SIZE);
u32Offset += PER_UINT_SIZE;
} else {
sprintf(&szText[u32Offset], "%s", "零");
u32Offset += PER_UINT_SIZE;
}
return u32Offset;
}
static inline int DecimalToText(double f64Value, char *szText) {
char szValue[DECIMAL_UINT_LEN] = {0};
unsigned short u16Idx = 0;
unsigned int u32Offset = 0;
unsigned int u32Decimal = 0;
unsigned int u32Precision = 0;
unsigned int u32Base = 0;
unsigned int u32Integer = 0;
char *szNumStr[10] = { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
// 基准放大倍数,通过放大将小数转为整数
u32Base = pow(10, DECIMAL_UINT_LEN);
u32Integer = (unsigned int)f64Value;
// 基准放大倍数基础上,再放大10倍,用于精度保留,
// (u32Base * 10) 一定要加上括号,要确保浮点型运算一次性放大,
// 如果f64Value * u32Base * 10, 仍然会引起精度丢失(截断:比如19.99->19.98)
u32Decimal = (unsigned int)(f64Value * (u32Base * 10) - u32Integer * (u32Base * 10));
u32Decimal /= 10;
printf("#DecimaToText: Target:%lf, Base:%d, Interger:%u, Decimal:%u\n",
f64Value, u32Base, u32Integer, u32Decimal);
u32Base /= 10;
for (int i = 0; i < DECIMAL_UINT_LEN; i++) {
if ((i == (DECIMAL_UINT_LEN - 1)) && u32Decimal) {
szValue[u16Idx++] = u32Decimal;
break;
}
if (u32Decimal < u32Base) {
szValue[u16Idx++] = 0;
} else {
szValue[u16Idx++] = u32Decimal / u32Base;
u32Decimal = u32Decimal % u32Base;
if (u32Decimal == 0)
break;
}
u32Base /= 10;
}
if (!u16Idx)
return 0;
sprintf(&szText[u32Offset], "%s", "点");
u32Offset += PER_UINT_SIZE;
for (unsigned short i = 0; i < u16Idx; i++) {
memcpy((szText + u32Offset), szNumStr[szValue[i]], PER_UINT_SIZE);
u32Offset += PER_UINT_SIZE;
}
return u32Offset;
}
static inline int DoubleToText(double f64Target, char *szText) {
unsigned int u32Integer;
unsigned int u32Offset;
unsigned int u32Tmp;
int s32Ret = 0;
bool bActive = false;
char *szUintStr[INTEGER_UINT_LEN] = {"万", "千", "百", "十", NULL};
unsigned int szUintValue[INTEGER_UINT_LEN] = {10000, 1000, 100, 10, 0};
if (f64Target >= 100000000.0f) {
printf("OverMaxValue:100000000.0f\n");
return -1;
}
u32Offset = 0;
u32Integer = (unsigned int)f64Target;
// 整数部分转为字符串
for (int i = 0; i < INTEGER_UINT_LEN; i++) {
// 个位数特殊处理
if (i == (INTEGER_UINT_LEN - 1)) {
s32Ret = IntegerToText(u32Integer, szText + u32Offset);
if (s32Ret < 0)
return -1;
u32Offset += s32Ret;
break;
}
// 十位数及以上位数处理
if (u32Integer < szUintValue[i]) {
if (bActive) {
sprintf(&szText[u32Offset], "%s", "零");
u32Offset += PER_UINT_SIZE;
}
} else {
u32Tmp = u32Integer / szUintValue[i];
u32Integer = u32Integer % szUintValue[i];
s32Ret = IntegerToText(u32Tmp, szText + u32Offset);
if (s32Ret < 0)
return -1;
u32Offset += s32Ret;
sprintf(&szText[u32Offset], "%s", szUintStr[i]);
u32Offset += PER_UINT_SIZE;
bActive = true;
if (u32Integer == 0)
break;
}
}
// 小数位处理
if (f64Target > u32Integer) {
s32Ret = DecimalToText(f64Target, szText + u32Offset);
if (s32Ret < 0)
return -1;
u32Offset += s32Ret;
}
// return total size
return u32Offset;
}
int main()
{
double f64Value = 0.0f;
char szText[1024] = {0};
while (1) {
printf("#Please input num(double):\n");
scanf("%lf", &f64Value);
memset(szText, 0, sizeof(szText));
DoubleToText(f64Value, szText);
printf("Resutl:%f -> %s\n", f64Value, szText);
}
return 0;
}
编译
gcc 直接编译,连接数学库,形如:gcc -o test test.c -lm