提问人:Costava 提问时间:4/24/2023 更新时间:4/24/2023 访问量:118
浮点数和双精度值的可能最长的 %a 字符串?
Longest possible %a string for floats and doubles?
问:
将说明符与函数系列一起使用时,浮点数和双精度值的最长字符串是多少?%a
printf
假设 float 和 double 是 IEEE-754 的 32 位和 64 位浮点。
更具体地说,以下函数需要多大才能不被超载(请注意,这也会写一个 null 终止符):buf
buf
sprintf
#include <stdio.h>
void StringFromFloat(char *buf, float f) {
sprintf(buf, "%a", f);
}
void StringFromDouble(char *buf, double d) {
sprintf(buf, "%a", d);
}
我假设最大长度没有区别。%A
答:
int main() {
int c;
printf("%a%n\n", -M_PI, &c);
printf("%d\n", c+1);
printf("%a%n\n", -DBL_MAX, &c);
printf("%d\n", c+1);
printf("%a%n\n", -DBL_MIN, &c);
printf("%d\n", c+1);
}
发出
-0x1.921fb54442d18p+1
22
-0x1.fffffffffffffp+1023
25
-0x1p-1022
11
https://coliru.stacked-crooked.com/a/9c8e5f7a059b66a5
如果我们假设格式是标准的,并且 dobules 是 IEEE-754 64 位,那么答案似乎是长度为 25(包括尾随的 null)
IEEE-754 64 位类型使用符号位(1 个字符)、52 位尾数(13 个十六进制字符)和 11 位指数(5 个十六进制字符)。这与上面的结果(、 和 )相匹配,这意味着其他 6 个字节是 ~标准格式(、 和-
fffffffffffff
+1023
0x1.
p
'\0'
)
评论
%n
"%n"
c
c + 1
最长的字符串
是多少,需要多大buf
通常,对字符串的长度进行评估,所需的缓冲区大小比长度多 1。最大长度将在下面讨论。为尺寸添加一个。
"%a"
具有各种实现定义的详细信息。因此,代码在根据以下计算假设最大长度时应非常小心。谨慎的代码将处理一些额外的字符。
合理估计:以下各项的总和:
最长的有效长度,也许是 -1.xxx 的形式......xxx,其中 x 是十六进制数字。若:。
FLT_RADIX==2
1 /* sign */ + 2 /* 0x */ + 1 /* lead digit */ + 1 /* . */ + roundup((xxx_MANT_DIG-1)/4)
最长指数,它是值或 的十进制 2 的幂。
xxx_TRUE_MIN
1 /* p */ + 1 /* sign */ + roundup(log2(-xxx_MIN_EXP + xxx_MANT_DIG))
对于普通双人间
:
significand length: `1 + 2 + 1 + 1 + ru((53-1)/4)` --> 18
exponent length: `1 + 1 + ru(log10(- -1021 + 53))` --> 6
sum: 24
对于普通浮点:
significand length: `1 + 2 + 1 + 1 + ru((24-1)/4)` --> 11
exponent length: `1 + 1 + ru(log10(- -125 + 24))` --> 5
sum: 16
考虑 NAN的有效负载可能以某种有趣的方式格式化,超过了上述总和。
示例:NAN w/payload: .C 指定有效负载和字符:[0-9A-Za-z_],但对其长度保持沉默。一种实现使用有效负载作为十进制值。double
"-NAN4503599627370495"
1 /* sign */ + 3 /* NAN */ + 16 /* decimal digits = 20
合理的 52 位有效负载应不超过 52 个有效负载字符。我从未见过比上述 20 个更长的。
1 /* sign */ + 3 /* NAN */ + 52 /* binary digits = 56
C 规范 关于 , :"%a"
"%A"
表示浮点数的参数以 [-]0xh.hhhhp±d 样式进行转换,其中小数点前有一个十六进制数字(如果参数是规范化浮点数且未指定,则为非零) character)和等于精度后的十六进制位数;如果精度缺失且为 2 的幂,则精度足以精确表示值;如果精度缺失且不是幂 的 2,则精度足以区分 类型的值,但尾随零可以省略;如果精度为零且未指定 # 标志,则不显示小数点字符。字母 abcdef 用于转换,字母 ABCDEF 用于 A 转换。A 转换说明符生成一个带有 X 和 P 的数字,而不是 x 和 p。指数始终包含至少一个数字,并且仅包含表示 2 的十进制指数所需的更多数字。如果该值为零,则指数为零。
表示无穷大或 NaN 的参数以 f 或 F 转换说明符的样式进行转换。
C17dr § 7.21.6.1 8double
FLT_RADIX
FLT_RADIX
double
double
评论
MANT_DIG
pow(2, -1074)
0x0.0000000000001p-1022
"0x1.0000000000000p-1074"
fprintf()
以下函数需要多大才能不被超支?
buf
buf
请考虑不要尝试查找最小最大缓冲区大小,而是将大小传递到函数中,并防止任何缓冲区溢出。@Andrew亨勒snprintf()
void StringFromFloat(size_t sz, char *buf, float f) {
volatile int len = snprintf(buf, sz, "%a", f);
assert(len >= 0 && (unsigned) len < sz);
}
如果调用代码想要传入被认为是始终有效的缓冲区大小,请使用各种宏计算预期的最大大小。
有关,请参阅此以获取更多想法。LOG10_PR()
#include <float.h>
#define LOG10_PR(x) (1 + (x)>9 + (x)>99 + (x)>999 + (x)>9999 + (x)>99999)
#define PRINTF_A_SIZE_FLT ( \
5 /* -0x1. */ + (FLT_MANT_DIG-1+3)/4 + \
2 /* p- */ + LOG10_PR(FLT_MANT_DIG - FLT_MIN_EXP) + \
1 /* \0 */)
char buf[PRINTF_A_SIZE_FLT];
StringFromFloat(sizeof buf, buf, some_float);
或者,如果不需要几个额外的字节,则要慷慨大方,并使用对象的位宽来缩放缓冲区大小。
char buf[sizeof some_float * CHAR_BIT + 1];
StringFromFloat(sizeof buf, buf, some_float);
评论
sprintf()
snprintf()
snprintf()
malloc()
asprintf()
snprintf()
sprintf()
snprintf()
volatile int len = snprintf(...,n,...); assert(len >= 0 && (unsigned) len < n);
char buf[sizeof(double)*CHAR_BIT + 1];
snprintf()
snprintf()
snprintf()
char buf[sizeof floating_point_object * CHAR_BIT + 1];