C++中的浮点数转二进制

4

我想知道在C++中是否有一种方法可以使用char来表示浮点数?

例如:

int main()  
{  
    float test = 4.7567;  
    char result = charRepresentation(test);  
    return 0;  
}  

我读到可能可以使用bitset来做到这一点,但我不是很确定。

假设我的浮点变量在二进制中为01001010 01001010 01001010 01001010。

如果我想要一个由4个元素组成的char数组,第一个元素将是01001010,第二个元素将是01001010,依此类推。

我能否用由4个元素组成的char数组表示浮点变量?


3
您期望result包含什么值? - James McNellis
3
你想要达成什么目标? - Potatoswatter
顺便说一下,浮点数已经是“二进制”的了。至少对于OP正在使用的任何实际计算机而言。 - dash-tom-bang
除非说明你想对表示法做什么,否则“表示法”的概念是没有意义的。如果你只需要知道浮点数与零是否不同,那么你可以将其表示为布尔值。你打算用 result 做什么? - sigfpe
抱歉,问题中缺少一部分。如果一个浮点数占用32位,而一个字符占用8位,我能否用由4个元素组成的字符数组表示该浮点数? 结果应该包含以下元素... char result[4],其中result = test; - Eric
对于你最后一个问题,“我能用4个元素的char数组表示float变量吗?”:你可以在任何4个字节中容纳一个float。单词“表示”可能有你不想要的含义。 - egrunin
7个回答

9
我猜您想表达的意思是:
int main()  
{  
    float test = 4.7567; 
    char result[sizeof(float)];

    memcpy(result, &test, sizeof(test));

    /* now result is storing the float,
           but you can treat it as an array of 
           arbitrary chars

       for example:
    */
    for (int n = 0; n < sizeof(float); ++n) 
        printf("%x", result[n]);

    return 0;  
}  
< p > 编辑后添加:所有指出你不能将float压缩成8位的人当然是正确的,但实际上,OP正在追求理解,即float,像所有原子数据类型一样,最终都是一个简单的连续字节块。这对于所有新手来说并不明显。


我会在这里添加一些内容来解释"sizeof"是如何计算你可以放入一个浮点数中的字符数量,并且你会得到多个字符作为结果。 - deworde
3
你也可以使用reinterpret_cast<char*>(test)来达到类似的效果。 - starblue
2
@starblue; 那应该是 reinterpret_cast<char*>(&test) 对吧? - falstro
这是一个重要的基础知识,但如果可以避免的话,请不要编写这样的代码。从浮点数到字节的转换因平台而异。请参阅http://en.wikipedia.org/wiki/Endianness。 - Potatoswatter
实际上,更合理的获取二进制表示的方法是reinterpret_cast<int*>(&test),假设int和float的字节顺序相同。我曾经用它来创建NaN值。@roe: 对的。 - starblue
显示剩余6条评论

1

你最好创建自定义的浮点类型,其字节大小较小。或者使用字符作为定点小数。但在所有情况下,这都会导致显著的精度损失。


尽管精度损失可能不重要,这取决于数据需求。例如,如果您只需要表示1.0和0.0以及254个中间步骤,则8位就足够了。 - dash-tom-bang

1

使用联合是干净易懂的

union
{
  float f;
  unsigned int ul;
  unsigned char uc[4];
} myfloatun;
myfloatun.f=somenum; printf("0x%08X\n",myfloatun.ul);

从编译器的角度来看,比指针更安全。Memcpy工作正常。

编辑

好吧,好吧,这里有完全有效的例子。是的,如果你不注意编译器如何分配联合并填充或对齐它,它可能会出问题,这就是为什么一些/许多人说这种方式使用联合是危险的原因。然而,其他选择被认为是安全的吗?

阅读一些关于C ++的内容,它自己也存在与联合相关的问题,并且联合可能根本无法工作。如果您真的指的是C++而不是C,则这可能是不好的。如果你说了“Kleenex”并指的是纸巾,那么这可能有效。

#include #include #include typedef union { float f; unsigned char uc[4]; } FUN;
void charRepresentation ( unsigned char *uc, float f) { FUN fun;
fun.f=f; uc[0]=fun.uc[3]; uc[1]=fun.uc[2]; uc[2]=fun.uc[1]; uc[3]=fun.uc[0]; }
void floatRepresentation ( unsigned char *uc, float *f ) { FUN fun; fun.uc[3]=uc[0]; fun.uc[2]=uc[1]; fun.uc[1]=uc[2]; fun.uc[0]=uc[3]; *f=fun.f; }
int main() { unsigned int ra; float test; char result[4]; FUN fun;
if(sizeof(fun)!=4) { printf("It aint gonna work!\n"); return(1); }
test = 4.7567F; charRepresentation(result,test); for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
test = 1.0F; charRepresentation(result,test); for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
test = 2.0F; charRepresentation(result,test); for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
test = 3.0F; charRepresentation(result,test); for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
test = 0.0F; charRepresentation(result,test); for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
test = 0.15625F; charRepresentation(result,test); for(ra=0;ra<4;ra++) printf("0x%02X ",(unsigned char)result[ra]); printf("\n");
result[0]=0x3E; result[1]=0xAA; result[2]=0xAA; result[3]=0xAB; floatRepresentation(result,&test); printf("%f\n",test);
return 0; }

输出的结果看起来像这样

gcc fun.c -o fun
./fun
0x40 0x98 0x36 0xE3
0x3F 0x80 0x00 0x00
0x40 0x00 0x00 0x00
0x40 0x40 0x00 0x00
0x00 0x00 0x00 0x00
0x3E 0x20 0x00 0x00
0.333333

您可以通过手动验证,或查看此网站,因为我直接从中取出了示例,输出与预期相匹配。

http://en.wikipedia.org/wiki/Single_precision

编程相关内容:你永远不应该使用指针指向内存并用不同类型查看它。我从来没有理解为什么这种做法经常被使用,特别是在结构体中。

int broken_code ( void )
{
    float test;
    unsigned char *result
test = 4.567; result=(unsigned char *)&test
//在此处对结果进行操作
test = 1.2345;
//在此处对结果进行操作
return 0; }

这段代码99%的时间都能正常工作,但不是100%的时间。当你最不希望它失败并且在最糟糕的时候,比如你最重要的客户收到它的第二天,它就会失败。优化器会在这种编码风格下吃掉你的午餐。是的,我知道大多数人都这样做,并且被教导这样做,也许从未受过伤...但这只会让它最终发生时更加痛苦,因为现在你知道它可能会失败(使用像gcc这样的流行编译器,在像个人电脑这样的常见计算机上)。

在使用此方法测试fpu时,程序化构建特定的浮点数/模式时,我看到了这种失败情况,我转而采用联合方法,到目前为止从未失败过。按定义,联合中的元素共享相同的存储块,编译器和优化器不会混淆该共享存储块中的两个项目...在同一共享存储块中。使用上述代码时,您依赖于一个假设,即每个变量的使用背后都有非寄存器内存存储,并且在下一行代码之前将所有变量写回该存储中。如果您从不进行优化或使用调试器,则可以正常运行。在这种情况下,优化器不知道结果和测试共享相同的内存块,这是问题/错误的根源。要进行指针游戏,您必须开始在所有内容上放置volatile,就像联合一样,您仍然必须了解编译器的对齐和填充方式,您仍然必须处理端点。

问题是通用的,编译器不知道这两个项目共享同一内存空间。对于上面的特定琐碎示例,我观察到编译器优化了将数字赋值给浮点变量的过程,因为该值/变量从未被使用。该变量的存储地址被使用,如果您要printf *result数据,则编译器不会优化掉result指针,因此不会优化掉测试地址,也不会优化掉test的存储,但在这个简单的示例中,数字4.567和1.2345可能永远不会进入编译后的程序。我还看到编译器分配了test的存储空间,但将数字分配给浮点寄存器,然后从未使用该寄存器,也没有将该寄存器的内容复制到已分配的存储空间中。它失败的原因对于更不琐碎的示例可能更难以理解,通常与寄存器分配和驱逐有关,改变一行代码就可以使其正常工作,改变另一行代码就会导致错误。

Memcpy

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void charRepresentation ( unsigned char *uc, float *f) { memcpy(uc,f,4); }
void floatRepresentation ( unsigned char *uc, float *f ) { memcpy(f,uc,4); }
int main() { unsigned int ra; float test; unsigned char result[4];
ra=0; if(sizeof(test)!=4) ra++; if(sizeof(result)!=4) ra++; if(ra) { printf("这不会起作用\n"); return(1); }
test = 4.7567F; charRepresentation(result,&test); printf("0x%02X ",(unsigned char)result[3]); printf("0x%02X ",(unsigned char)result[2]); printf("0x%02X ",(unsigned char)result[1]); printf("0x%02X\n",(unsigned char)result[0]);
test = 0.15625F; charRepresentation(result,&test); printf("0x%02X ",(unsigned char)result[3]); printf("0x%02X ",(unsigned char)result[2]); printf("0x%02X ",(unsigned char)result[1]); printf("0x%02X\n",(unsigned char)result[0]);
result[3]=0x3E; result[2]=0xAA; result[1]=0xAA; result[0]=0xAB; floatRepresentation(result,&test); printf("%f\n",test);
return 0; }
gcc fcopy.c -o fcopy
./fcopy
0x40 0x98 0x36 0xE3
0x3E 0x20 0x00 0x00
0.333333

根据我的上述评论,我可能会引起争议,取决于你选择站在哪一边。也许memcpy是您最安全的路线。您仍然必须非常了解编译器,并管理好字节序。编译器不应该搞砸memcpy,它应该在调用之前将寄存器存储到内存中,并按顺序执行。


我有一个错误吗?这段代码没有经过完全测试,只是一种“尝试类似于此但没有错误”的代码,就像海报上的那样,有点像这个但忽略了错误的代码。多年来,我一直在使用联合体来检查浮点位,不同的编译器,不同的平台,从未遇到过任何问题。至少在C中,联合体不能与C++一起使用吗? - old_timer
实际上,memcpy方法失败的原因与指向内存的指针方法失败的原因相同,如果您想避免编译器中的未定义行为,就必须超越编译器的范围,例如写入文件。如果您对不到100%可靠的内容感到满意,那么任何这些方法都可以使用。 - old_timer

0
int main()  
{  
    float test = 4.7567;  
    char result = charRepresentation(test);  
    return 0;  
}

如果我们忽略你的浮点数是一个浮点数,并将47567转换为二进制,我们得到10111001 11001111。这是16位,是char(8位)大小的两倍。浮点数通过存储符号位(+或-),指数(在哪里放小数点,在这种情况下为10 ^ -1)以及有效数字(47567)来存储其数字。 字符中没有足够的空间来存储浮点数。
另外,请考虑一个字符只能存储256个不同的值。具有四位小数精度,1和4.7567之间的值远远超过256个,甚至是4和4.7567之间的值。由于您不能区分超过256个不同的值,因此没有足够的空间来存储它。
您可以构思一些“翻译”从浮点数到字符的东西,通过限制自己的极小范围和仅一两位小数*,但我无法想出任何你会想要这样做的原因。

*在char中,您可以存储0到256之间的任何值,因此,如果您始终将char中的值乘以10 ^ -1或10 ^ -2(您只能使用其中一个选项,因为没有足够的空间来存储指数),则可以存储0到25.6或0到2.56之间的任何数字。不过我不知道这有什么用。


0

你只能以一种部分的方式进行操作,这样就无法完全恢复原始浮点数。通常,这被称为量化,根据您的要求选择一个好的量化方法是一门艺术。例如,用于表示像素中的R、G和B的浮点值将在显示在屏幕上之前转换为char。

或者,可以将一个浮点数完整地存储为四个字符,每个字符存储有关原始数字的一些信息,这也很容易实现。


0

你可以使用2位整数和5位小数部分(如果要无符号则为6位)创建一个固定点值,用于表示该数字。这将能够精确存储大约4.76的值。除非您使用了256个条目的ROM查找表,在其中将信息存储在数字本身之外并在翻译器中进行转换,否则您没有足够的空间来表示更精确的数字。


0

C语言中的char类型通常只有8位。这会带来两个基本问题。首先,几乎所有现有的浮点数处理器都支持IEEE浮点数。这意味着浮点数值需要32位或64位。一些处理器支持其他非标准大小,但我所知道的唯一的是80位。我从未听说过有支持仅8位浮点数的处理器。因此,你无法获得对8位浮点数的硬件支持。

更重要的是,你将无法从8位浮点数中获得很多数字。请记住,一些位用于表示指数。你几乎没有剩余的精度可用于数字。

也许你想了解的是定点数?那可以在一个字节内完成。


标准(IEEE 754-2008)的尺寸还包括半精度(16位)和四倍精度(128位)。但没有8位。 - dan04

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接