我一直绊倒printf()函数系列的格式说明符.我想要的是能够在小数点后打印一个最大给定位数的double(或float).如果我使用:
printf("%1.3f", 359.01335); printf("%1.3f", 359.00999);
我明白了
359.013 359.010
而不是期望的
359.013 359.01
有谁能够帮我?
使用普通printf
格式说明符无法做到这一点.你可以得到的最接近的是:
printf("%.6g", 359.013); // 359.013 printf("%.6g", 359.01); // 359.01
但是".6"是总的数字宽度
printf("%.6g", 3.01357); // 3.01357
打破它.
您可以做的是对sprintf("%.20g")
字符串缓冲区的数字,然后操纵字符串只有N个字符超过小数点.
假设您的数字在变量num中,以下函数将删除除第一N
个小数之外的所有小数,然后去除尾随零(如果它们都是零,则删除小数点).
char str[50]; sprintf (str,"%.20g",num); // Make the number. morphNumericString (str, 3); : : void morphNumericString (char *s, int n) { char *p; int count; p = strchr (s,'.'); // Find decimal point, if any. if (p != NULL) { count = n; // Adjust for more or less decimals. while (count >= 0) { // Maximum decimals allowed. count--; if (*p == '\0') // If there's less than desired. break; p++; // Next character. } *p-- = '\0'; // Truncate string. while (*p == '0') // Remove trailing zeros. *p-- = '\0'; if (*p == '.') { // If all decimals were zeros, remove ".". *p = '\0'; } } }
如果你对截断方面不满意(它会0.12399
变成0.123
而不是将其舍入到0.124
),你实际上可以使用已经提供的舍入设施printf
.您只需要预先分析数字以动态创建宽度,然后使用它们将数字转换为字符串:
#includevoid nDecimals (char *s, double d, int n) { int sz; double d2; // Allow for negative. d2 = (d >= 0) ? d : -d; sz = (d >= 0) ? 0 : 1; // Add one for each whole digit (0.xx special case). if (d2 < 1) sz++; while (d2 >= 1) { d2 /= 10.0; sz++; } // Adjust for decimal point and fractionals. sz += 1 + n; // Create format string then use it. sprintf (s, "%*.*f", sz, n, d); } int main (void) { char str[50]; double num[] = { 40, 359.01335, -359.00999, 359.01, 3.01357, 0.111111111, 1.1223344 }; for (int i = 0; i < sizeof(num)/sizeof(*num); i++) { nDecimals (str, num[i], 3); printf ("%30.20f -> %s\n", num[i], str); } return 0; }
整点nDecimals()
在这种情况下能够正常工作了字段宽度,然后格式化使用基于该格式字符串的数量.测试工具main()
显示了这一点:
40.00000000000000000000 -> 40.000 359.01335000000000263753 -> 359.013 -359.00999000000001615263 -> -359.010 359.00999999999999090505 -> 359.010 3.01357000000000008200 -> 3.014 0.11111111099999999852 -> 0.111 1.12233439999999995429 -> 1.122
一旦你有正确的舍入值,你可以再次传递它morphNumericString()
来删除尾随零只需更改:
nDecimals (str, num[i], 3);
成:
nDecimals (str, num[i], 3); morphNumericString (str, 3);
(或者morphNumericString
在结束时调用,nDecimals
但在这种情况下,我可能只是将两者合并为一个函数),最后你得到:
40.00000000000000000000 -> 40 359.01335000000000263753 -> 359.013 -359.00999000000001615263 -> -359.01 359.00999999999999090505 -> 359.01 3.01357000000000008200 -> 3.014 0.11111111099999999852 -> 0.111 1.12233439999999995429 -> 1.122
要删除尾随零,您应该使用"%g"格式:
float num = 1.33; printf("%g", num); //output: 1.33
在问题澄清了一点之后,抑制零并不是唯一被问到的东西,但是也需要将输出限制为三位小数.我认为单独使用sprintf格式字符串无法做到这一点.正如Pax Diablo指出的那样,需要字符串操作.
我喜欢R.略微调整的答案:
float f = 1234.56789; printf("%d.%.0f", f, 1000*(f-(int)f));
'1000'决定了精度.
动力为0.5舍入.
编辑
好吧,这个答案被编辑了几次,我忘记了几年前我的想法(原来它并没有满足所有标准).所以这是一个新版本(填写所有标准并正确处理负数):
double f = 1234.05678900; char s[100]; int decimals = 10; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf("10 decimals: %d%s\n", (int)f, s+1);
和测试用例:
#import#import #import int main(void){ double f = 1234.05678900; char s[100]; int decimals; decimals = 10; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf("10 decimals: %d%s\n", (int)f, s+1); decimals = 3; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf(" 3 decimals: %d%s\n", (int)f, s+1); f = -f; decimals = 10; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf(" negative 10: %d%s\n", (int)f, s+1); decimals = 3; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf(" negative 3: %d%s\n", (int)f, s+1); decimals = 2; f = 1.012; sprintf(s,"%.*g", decimals, ((int)(pow(10, decimals)*(fabs(f) - abs((int)f)) +0.5))/pow(10,decimals)); printf(" additional : %d%s\n", (int)f, s+1); return 0; }
并且测试的输出:
10 decimals: 1234.056789 3 decimals: 1234.057 negative 10: -1234.056789 negative 3: -1234.057 additional : 1.01
现在,满足所有标准:
零后面的最大小数位数是固定的
尾随零被删除
它在数学上是对的(对吧?)
当第一个小数为零时,也可以工作(现在)
不幸的是,这个答案是双线的,因为sprintf
不会返回字符串.