VS2019中C++的double值snprintf舍入规则疑问
snprintf格式化double舍入结果不符合预期的原因
问题背景
编写了如下C程序,尝试用snprintf将多个double值格式化为保留两位小数的字符串:
#include <stdio.h> #include <stdlib.h> int main() { char buf[32]; snprintf(buf, 32, "%.2f", 447.065); printf("447.065 --> %s\n", buf); snprintf(buf, 32, "%.2f", 447.165); printf("441.165 --> %s\n", buf); snprintf(buf, 32, "%.2f", 447.265); printf("447.265 --> %s\n", buf); snprintf(buf, 32, "%.2f", 447.365); printf("441.365 --> %s\n", buf); snprintf(buf, 32, "%.2f", 447.465); printf("447.465 --> %s\n", buf); snprintf(buf, 32, "%.2f", 447.565); printf("441.565 --> %s\n", buf); snprintf(buf, 32, "%.2f", 447.665); printf("447.665 --> %s\n", buf); snprintf(buf, 32, "%.2f", 447.765); printf("441.765 --> %s\n", buf); snprintf(buf, 32, "%.2f", 447.865); printf("441.865 --> %s\n", buf); snprintf(buf, 32, "%.2f", 447.965); printf("441.965 --> %s\n", buf); }
程序输出:
447.065 --> 447.06 441.165 --> 447.17 447.265 --> 447.26 441.365 --> 447.37 447.465 --> 447.46 441.565 --> 447.56 447.665 --> 447.67 441.765 --> 447.76 441.865 --> 447.87 441.965 --> 447.96
原本预期snprintf采用银行家舍入(四舍六入五成双),但实际输出与该算法的预期结果不符,疑惑其中原因。
原因解析
核心问题出在浮点数的二进制存储精度限制:
- 像
447.065这类十进制小数,无法用二进制浮点数(double类型)精确表示,编译器会将其转换为最接近的二进制近似值存储。 - 比如
447.065的实际存储值略小于十进制的447.065,当snprintf按%.2f格式化时,这个近似值的第三位小数实际上不足0.005,因此会被舍去,得到447.06; - 而
447.165的实际存储值略大于十进制的447.165,第三位小数超过0.005,因此会进位得到447.17。
银行家舍入(四舍六入五成双)的规则仅会在待舍入的数值恰好是中间值(即精确等于x.xx5)时触发,但你代码中写的这些十进制字面量,根本无法被double精确存储,snprintf处理的是这些近似值,自然不会符合你基于十进制字面量预期的银行家舍入结果。
内容的提问来源于stack exchange,提问作者zentrunix
相关产品推荐
相关产品推荐

