float变量和编译的大小

时间:2018-01-09 17:42:05

标签: c gcc floating-point

我在努力理解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能否增加一种类型的容量? (管理幕后的多个变量以允许我们这样的事情)

5 个答案:

答案 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 representationfloat的最大值可以表示的是由常量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,其类型可能 281474976710655long。 (顺便提一下,该值可以存储在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)