现在我真正想知道的是浮点数值的相同情况。要将任何“double”或“float”值表示为字符串所需的最大字符数是多少?
假设使用常规C/C++ double(IEEE 754)和常规十进制展开(即没有%e printf-formatting)。
我甚至不确定非常小的数字(即0.234234)是否会比表示整数的双倍更长?
C语言中的标准头文件<float.h>
或C++中的<cfloat>
包含了与浮点类型的范围和其他度量有关的常量,其中之一是DBL_MAX_10_EXP
,表示表示所有double
值所需的最大10的幂指数。由于表示1eN
需要N+1
个数字,并且可能还有一个负号,所以答案是
int max_digits = DBL_MAX_10_EXP + 2;
假设指数大于表示最大有效数字所需的位数,否则会有小数点和更多数字。
更正
实际上,最长的数字是最小可表示的负数:它需要足够的数字来覆盖指数和有效数字。这个值为-pow(2, DBL_MIN_EXP - DBL_MANT_DIG)
,其中DBL_MIN_EXP
为负数。很容易看出(并归纳证明),非科学计数法的十进制表示形式-pow(2,-N)
需要3+N
个字符("-0."
,后跟N
个数字)。因此答案是
int max_digits = 3 + DBL_MANT_DIG - DBL_MIN_EXP
对于一个64位的IEEE双精度浮点数,我们有
DBL_MANT_DIG = 53
DBL_MIN_EXP = -1023
max_digits = 3 + 53 - (-1023) = 1079
double
值(即以"%f"
格式)所需的最大字符数将为-DBL_MIN
的值(即-0x1p-1022,假设您的double
是二进制64 IEEE 754)。为此,您需要确切地325个字符。也就是说:DBL_DIG + abs(DBL_MIN_10_EXP)+ strlen("-0.")
。当然,这是因为log10(fabs(DBL_MIN))
为308,它也是abs(DBL_MIN_10_EXP)+1
(+1是因为小数点左边的前导数字),这是前导零位于显着数字左侧的数量。 - Greg A. Woodsprintf("%0.320lf\n", -DBL_MIN);
- David Schwartz| Single| Double | Extended | Quad |
|:-----:|:------:|:--------:|:-----:|
| 16 | 24 | 30 | 45 |
这些数字基于以下计算:
| Single| Double | Extended | Quad |
|:-----:|:------:|:--------:|:-----:|
| 9 | 17 | 21 | 36 |
* Quantities listed in decimals.
十进制计数基于以下公式:最多为 Ceiling(1 + NLog_10(2)) 位小数,其中 N 是整数部分的位数*。
| Single| Double | Extended | Quad |
|:-----:|:------:|:--------:|:-----:|
| 5 | 5 | 7 | 7 |
* Standard format is `e-123`.
打印浮点数的最快算法是Grisu2算法,该算法在研究论文《快速准确地打印浮点数》中有详细介绍。我能找到的最佳基准测试可以在这里找到。
snprintf()
来检查所需的字符数。
snprintf()
返回打印传递给它的任何内容所需的字符数。/* NOT TESTED */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char dummy[1];
double value = 42.000042; /* or anything else */
int siz;
char *representation;
siz = snprintf(dummy, sizeof dummy, "%f", value);
printf("exact length needed to represent 'value' "
"(without the '\\0' terminator) is %d.\n", siz);
representation = malloc(siz + 1);
if (representation) {
sprintf(representation, "%f", value);
/* use `representation` */
free(representation);
} else {
/* no memory */
}
return 0;
}
double value = ??;
的数字是什么。 - DrBeco1024 远远不够,最小的负 double 值有 1077 个十进制位数。以下是一些 Java 代码。
double x = Double.longBitsToDouble(0x8000000000000001L);
BigDecimal bd = new BigDecimal(x);
String s = bd.toPlainString();
System.out.println(s.length());
System.out.println(s);
1077
-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625
根据Greg A. Woods准确的评论,对接受的答案进行改进,需要更为保守但仍足够的字符数为3 + DBL_DIG + -DBL_MIN_10_EXP
(总共325个字符),其中3代表可能需要的前导“-0.”。如果使用C风格的字符串,则需添加一个空(null)字符('\0'
)以便创建充分大小(大小为326)的缓冲区:
#include <limits.h>
char buffer[4 + DBL_DIG + -DBL_MIN_10_EXP];
对于那些更喜欢 C++ 数值限制接口 的人,可以使用以下代码:
#include <limits>
char buffer[4 + std::numeric_limits<double>::digits10 + -std::numeric_limits<double>::min_exponent10];
当您将float/double转换为字符串时,可以通过设置精度来控制字符串表示中的数字位数。此时,最大数字位数将等于以您指定的精度为标准的std::numeric_limits<double> :: max()
的字符串表示。
#include <iostream>
#include <limits>
#include <sstream>
#include <iomanip>
int main()
{
double x = std::numeric_limits<double>::max();
std::stringstream ss;
ss << std::setprecision(10) << std::fixed << x;
std::string double_as_string = ss.str();
std::cout << double_as_string.length() << std::endl;
}
所以,具有精度为10的double
类型的最大位数为320位。
这取决于你所说的“表示”。十进制小数没有精确的浮点表示。当你将十进制小数转换为二进制小数再转换回十进制时,你没有精确的十进制表示,并且在二进制表示的末尾会有噪声位。
问题没有涉及从十进制开始,但所有源代码(和必须用户输入)都是十进制的,并涉及可能的截断问题。在这种情况下,“精确”是什么意思?
基本上,这取决于你的浮点表示。
如果你有48位的尾数,这大约需要16个十进制数字。指数可能是剩余的14位(大约5个十进制数字)。
经验法则是,位数约为十进制数字的3倍。
在打印任何十进制 double
值(即以 "%f"
格式)所需的最大字符数将是对于值 -DBL_MIN
(即 -0x1p-1022,假设二进制64位 IEEE 754 是您的 double
),需要确切的325个字符。这是: DBL_DIG + abs(DBL_MIN_10_EXP) + strlen("-0.")
。当然,这是因为 log10(fabs(DBL_MIN))
是308,这也是 abs(DBL_MIN_10_EXP)+1
(+1 是因为小数点左侧的前导数字),这是有效数字左侧的前导零的数量。
int lz; /* aka abs(DBL_MIN_10_EXP)+1 */
int dplaces;
int sigdig; /* aka DBL_DECIMAL_DIG - 1 */
double dbl = -DBL_MIN;
lz = abs((int) lrint(floor(log10(fabs(dbl)))));
sigdig = lrint(ceil(DBL_MANT_DIG * log10((double) FLT_RADIX)));
dplaces = sigdig + lz - 1;
printf("f = %.*f\n", dplaces, dbl);
printf("lz = %d, sigdig = %d, dplaces = %d, dbl = %g\n", lz, sigdig, dplaces, dbl);
则打印出"lz = 308, sigdig = 16, dplaces = 323, dbl = -2.22507e-308"。你收到了什么输出? - chux - Reinstate Monicalrint()
调用与 (int)
相比提供了很少的价值。移除它将增加清晰度。 - chux - Reinstate Monicasigdig
公式在 FLT_RADIX == 2
时会偏差 1。请参考 DBL_DECIMAL_DIG
的定义,这实际上是用来确定所有 double
的 significant digits 的。建议将 sigdig = DBL_DECIMAL_DIG;
(例如 17
) - chux - Reinstate MonicaDBL_MIN
是最小的正常值。 DBL_TRUE_MIN
是最小的非零双精度浮点数。当然,使用 DBL_MIN
将足以打印 有效数字 - 正如此答案所示。 - chux - Reinstate Monicasigdig
实际上就是DBL_DECIMAL_DIG - 1
。 - Greg A. Woods
1.#INF00
,最大的数字长度为286字节。所以我猜你用512字节应该没问题吧?(使用printf)。 - Rookieprintf
格式来格式化std::string
。所以如果我得到一个双精度浮点数,我需要创建一个char[X]
缓冲区。 - Tomáš Zato