HOWTO · C
用 C 的科学计数法打印数字
本文介绍如何在 C 语言中以科学计数法打印数字
本页内容
在 printf 中使用 %e 或 %E 转换说明符,即可将浮点值显示为科学记数法。小写转换会输出小写指数标记,大写转换则会输出大写标记。精度写法(如 %.3e)用于设置有效数的小数点后位数。
运行以下示例:
本节中的代码以及显示的标签或输出仍为英文。
#include <stdio.h>
int main(void) {
double values[] = {123456.789, -0.000012345};
printf("lowercase: %.3e\n", values[0]);
printf("uppercase: %.3E\n", values[1]);
return 0;
}
C 中的科学记数法原理
科学记数法将非零值写成有效数乘以 10 的某次幂。有效数的小数点前有一位非零数字;指数表示小数点移动了多少位。例如,123456 可以写成 1.23456 × 10^5,而 0.0000123 可以写成 1.23 × 10^-5。
这种记数法改变的是数值的显示方式,不会改变底层 float 或 double 的存储方式。二进制浮点类型的精度有限,因此许多十进制小数在内存中只能近似表示。打印更多位数可能会显示出存储值的更多近似细节,但不会提高存储值本身的精度。
使用 printf 打印科学记数法
e 和 E 转换会将 double 参数格式化为科学记数法。指数标记的大小写取决于转换符:%e 使用 e,%E 使用 E。指数中包含正负号,实现会输出足以表示指数的位数。如果要对照固定小数格式,可以使用 %f。POSIX fprintf 规范定义了 e 和 E 转换及其精度规则。
在开头的示例中,%.3e 和 %.3E 都会在有效数的小数点后显示三位数字。格式化后的数值会保留正负号。对于用固定小数格式不易阅读的极大或极小数值,科学记数法能让有效数保持简洁,并用指数表示数量级。
这些转换要求参数为 double。通过 printf 传入的 float 会提升为 double,但整数不会。如果要用科学记数法格式化整数值,应先显式转换,例如使用 (double)count,再传给 %e 或 %E。如果大整数超出 double 可表示的精度范围,转换时可能会丢失低位数字。
控制小数点后的位数
对于 %e 和 %E,精度表示小数点后的位数,而不是有效数字总数。因此,%.2e 会输出两位小数,通常合计三位有效数字。如果省略精度,默认会在小数点后输出六位。显示时会按指定精度对数值进行舍入,但存储的值不会改变。POSIX fprintf 规范说明了精度规则和六位小数的默认值。
应根据应用所需的显示格式或交换格式来选择精度。打印更多位数并不保证十进制字符串能精确表示二进制浮点值。如果需要指定有效数字的数量,选择精度时要把有效数开头的那一位也计算在内。
安全地格式化科学记数法字符串
如果需要存储格式化后的数值,或将它传给其他函数,请使用 snprintf 并指定缓冲区容量。容量包含结尾的空字符。snprintf 返回本来会写入的字符数,但不包括这个空字符;如果返回值大于或等于容量,表示输出已被截断。负返回值表示发生格式化错误。POSIX fprintf 规范定义了 snprintf 的返回值和截断行为。
运行以下独立示例:
本节中的代码以及显示的标签或输出仍为英文。
#include <stdio.h>
int main(void) {
double measurement = 0.000012345;
char text[64];
int length = snprintf(text, sizeof text, "%.3e", measurement);
if (length < 0) {
puts("formatting error");
return 1;
}
if ((size_t)length >= sizeof text) {
puts("buffer too small");
return 1;
}
printf("value: %s\n", text);
return 0;
}
snprintf 最多写入缓冲区可容纳的字符数;容量大于零时,它会为缓冲区添加结尾符。在将缓冲区内容视为完整的格式化结果之前,请先检查返回值。如果无法保证缓冲区足够大,应避免使用没有长度限制的 sprintf。
应该编写自定义科学记数法函数吗?
通常不需要。按照 C 库的格式化规则,%e 和 %E 已能处理有效数、指数、正负号、舍入以及零值。手写转换函数需要单独处理零,并将小于 1 的数以及大于或等于 10 的数规范化。此外,还必须考虑负数、舍入导致有效数变化的情况,以及数值类型的范围限制。只会反复将正数除以 10 的循环无法正确覆盖这些情况。
直接显示时优先使用 printf;需要有界的字符串格式化时使用 snprintf。只有在标准转换无法提供所需表示形式时,才考虑编写自定义格式化函数,并明确规定、检查它对完整输入范围的处理方式。对于常见的科学记数法,标准转换写法更简短,也更不容易出错。