我在努力理解gcc
的行为。对于我的架构,浮点数的大小为4个字节。但是我仍然可以在float中存储一个8字节的实数值,而我的编译器对它没有任何说明。
例如我有:
#include <stdio.h>
int main(int argc, char** argv){
float someFloatNumb = 0xFFFFFFFFFFFF;
printf("%i\n", sizeof(someFloatNumb));
printf("%f\n", someFloatNumb);
printf("%i\n", sizeof(281474976710656));
return 0;
}
我期望编译器侮辱我,或者显示某种类型的免责声明,因为我不应该能够做到这样的事情,至少我认为它是一种扭曲的巫术。
该程序只需运行:
4
281474976710656.000000
8
因此,如果我打印someFloatNumb
的大小,我得到4个字节,这是预期的。但受影响的价值并不是如下所示。
所以我有几个问题:
sizeof(variable)是否只是获取变量类型并返回sizeof(type),在这种情况下会解释结果?
/ gcc能否增加一种类型的容量? (管理幕后的多个变量以允许我们这样的事情)
答案 0 :(得分:3)
1)
sizeof(variable)是否只是获取变量类型并返回sizeof(type),在这种情况下会解释结果?
variable-length arrays除外,sizeof
不评估其操作数。所以,是的,所有关心的都是类型。因此sizeof(someFloatNumb)
为4,相当于sizeof(float)
。这解释了printf("%i\n", sizeof(someFloatNumb));
。
2)
[..]但是我仍然可以在float中存储一个8字节的实数值,而我的编译器对此一无所知。 / gcc能增长一种类型的容量吗? (管理幕后的多个变量以允许我们这样的事情)
没有。容量不会增长。你只是误解了如何表示/存储浮点数。 sizeof(float)
为4并不意味着
它不能存储超过2 ^ 32(假设1字节== 8位)。见Floating point representation。
float
的最大值可以表示的是由常量FLT_MAX
定义的(请参阅<float.h>
)。 sizeof(someFloatNumb)
只是产生对象(someFloatNumb
)在内存中占用的字节数,它不一定等于它可以表示的值的范围。
这解释了为什么printf("%f\n", someFloatNumb);
按预期打印值(并且没有自动“容量增长”)。
3)
printf(“%i \ n”,sizeof(281474976710656));
这稍微复杂一些。如前面(1)中所述,sizeof
只关心这里的类型。但281474976710656
的类型不一定是int
。
C标准根据可以表示值的最小类型定义整数常量的类型。有关说明,请参阅https://stackoverflow.com/a/42115024/1275169。
在我的系统281474976710656
上无法用int
表示,并且它存储在long int
中,这也可能是您系统中的情况。所以你看到的基本上等同于sizeof(long)
。
没有可移植的方法来确定整数常量的类型。但是,由于您使用的是gcc
,因此可以使用typeof
:
typeof(281474976710656) x;
printf("%s", x); /* deliberately using '%s' to generate warning from gcc. */
产生
警告:格式'%s'需要类型为'char *'的参数,但参数为2 类型'long int'[-Wformat =] printf(“%s”,x);
P.S:sizeof
会产生size_t
,其正确的格式说明符为%zu
。这就是你应该在第1和第3版printf语句中使用的内容。
答案 1 :(得分:2)
这不会存储“8字节”的数据,编译器会将该值转换为整数,然后转换为float
进行分配:
float someFloatNumb = 0xFFFFFFFFFFFF; // 6 bytes of data
由于float
可以表示较大的值,因此这不是什么大问题,但如果您只使用32位浮点数,则会失去很多精度。请注意,这里存在轻微但重要的区别:
float value = 281474976710656.000000;
int value = 281474976710655;
这是因为float
在精度不足时变为近似值。
标准C类型的容量不会“增长”。你必须使用"bignum" library。
答案 2 :(得分:1)
但是我仍然可以在float和我的编译器中存储一个8字节的实数值 什么也没说。
那不是发生了什么。
EXEC msdb.dbo.sp_send_dbmail
@profile_name = 'fpmetrics',
@recipients='Brian@Test.com',
@query = N'select i.ticket_number, i.status_1 as Status, i.title,
i.description, u.user_login as ''Assignee'', i.created_on from
fpscdb008_system.asgnmt a, fpscdb008_system.app_user u,
fpscdb008_ws_004.incidents i
where i.id = a.item_id
and a.item_defn_id = 12610
and u.app_user_id = a.app_user_id
and i.soft_delete_id = 0
and i.status_1 not in (''Closed'',''Resolved'',''Cancelled'')
Union
select s.ticket_number, s.status_1 as Status, s.title, s.description,
u.user_login as ''Assignee'', s.created_on from
fpscdb008_system.asgnmt a, fpscdb008_system.app_user u,
fpscdb008_ws_004.service_request s
where a.app_user_id = u.app_user_id
and a.item_defn_id = 7861
and s.id = a.item_id
and s.soft_delete_id = 0
and s.status_1 not in (''Closed'',''Resolved'',''Cancelled'');',
@attach_query_result_as_file = 1,
@query_attachment_filename = 'Values.csv',
@query_result_separator=',',
@subject = 'pie';
float someFloatNumb = 0xFFFFFFFFFFFF;
是一个整数常量。它的值以十进制表示,为0xFFFFFFFFFFFF
,其类型可能 281474976710655
或long
。 (顺便提一下,该值可以存储在48位,但大多数系统不具有48位整数类型,因此它可能存储在64位中,其中高16位将为零。 )
使用一种数字类型的表达式初始化不同数值类型的对象时,该值为转换。此转换不依赖于源表达式的大小,仅取决于其数值。对于整数到浮点转换,结果是与整数值最接近的表示。可能会有一些精度损失(在这种情况下,有)。有些编译器可能会选择警告精度损失,但转换完全有效,因此默认情况下您可能无法获得警告。
这是一个小程序来说明发生了什么:
long long
我系统的输出是:
#include <stdio.h>
int main(void) {
long long ll = 0xFFFFFFFFFFFF;
float f = 0xFFFFFFFFFFFF;
printf("ll = %lld\n", ll);
printf("f = %f\n", f);
}
正如您所看到的,转换已经失去了一些精确度。 ll = 281474976710655
f = 281474976710656.000000
是2的精确幂,浮点类型通常可以准确地表示这些。这两个值之间存在非常小的差异,因为您选择的整数值非常接近可以精确表示的整数值。如果我改变了值:
281474976710656
明显的精度损失要大得多:
#include <stdio.h>
int main(void) {
long long ll = 0xEEEEEEEEEEEE;
float f = 0xEEEEEEEEEEEE;
printf("ll = %lld\n", ll);
printf("f = %f\n", f);
}
答案 3 :(得分:0)
0xFFFFFFFFFFFF == 281474976710655
如果你使用该值初始化一个浮点数,它将最终成为
0xFFFFFFFFFFFF +1 == 0x1000000000000 == 281474976710656 == 1&lt;&lt; 48
这很容易适合4字节浮点数,简单的mantisse,小指数 但它确实没有存储正确的值(低一个),因为它很难存储在浮点数中。
注意&#34; + 1&#34;并不意味着增量。它结束了一个更高,因为表示只能与尝试值一样接近。你可以认为&#34;四舍五入到mantisse可以存储的任何多余的2的幂2&#34;。顺便说一句,Mantisse通常被解释为介于0和1之间的分数 越来越接近确实需要在mantisse中进行48位初始化;加上用于存储指数的任何位数;还有一些其他细节。
答案 4 :(得分:0)
查看打印的值... fig, ax = plt.subplots(figsize=(10,8))
ax = sns.boxplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='tfidf'])
ax = sns.swarmplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='tfidf'], color="0.25")
fig, ax = plt.subplots(figsize=(10,8))
ax = sns.boxplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='lda'])
ax = sns.swarmplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='lda'], color="0.25")
fig, ax = plt.subplots(figsize=(10,8))
ax = sns.boxplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='doc2vec'])
ax = sns.swarmplot(x="feedback", y="similarity_score", data=df[df.nlp_model=='doc2vec'], color="0.25")
plt.show()
是一个奇数值,但示例中打印的值是偶数。您正在为0xFFFF...FFFF
变量提供float
值,该值已转换为int
。转换正在失去精度,正如所使用的值所预期的那样,它不适合保留给目标变量尾数的23位。最后你得到一个近似值是值float
(下一个值,这是你使用的值的最接近的值,在他的回答中发布@Yunnosch)