我有一个家庭作业来模仿浮点角色,例如:
int y = /* ... */;
float x = (float)(y);
。 。 。但显然没有使用铸造。那很好,我不会有问题,除了我找不到任何具体的,具体的定义完全这样的演员阵容应该如何运作。
我编写了一个相当好的实现,但它偶尔也不匹配(例如,它可能在指数中加上三个值并用尾数填充尾数,但“基本事实”将会指数中的值为4,并用零填充尾数。事实上两者是等价的(sorta,无限序列)令人沮丧,因为位模式仍然是“错误的”。
当然,我从分散的网站上得到了模糊的东西,比如“向零走”,但老实说,我的搜索不断堵塞C新手问题(例如,“什么是演员?”,“我什么时候使用它?”)。所以,我找不到一个适用于明确定义指数和尾数的一般规则。
帮助?谢谢,
伊恩
答案 0 :(得分:2)
由于这是作业,我只会发布一些关于我认为是棘手的部分的注释 - 当整数的幅度大于浮点数的精度时,四舍五入。听起来你已经有了获得指数和尾数的基础知识的解决方案。
我假设您的浮动表示是IEEE 754,并且该舍入的执行方式与MSVC和MinGW相同:使用“银行家舍入”方案(我老实说不确定是否需要特定的舍入方案按照标准;这是我测试的虽然)。其余的讨论假设要转换的int大于0
。可以通过处理它们的绝对值并在最后设置符号位来处理负数。当然,0
需要特别处理(因为没有msb可以找到)。
由于尾数中有24位精度(包括msb的隐含1
),因此可以精确表示高达16777215
(或0x00ffffff
)的整数。没有什么特别的事情可以做,除了位移到正确的地方并根据班次计算正确的指数。
但是,如果int值中的精度超过24位,则需要进行舍入。我使用以下步骤执行舍入:
0
,则不需要再进行任何操作。尾数和指数可以保持不变。 1
,并且剩余的丢弃比特设置了一个或多个比特,则尾数需要递增。如果尾数溢出(超过24位,假设你还没有丢弃隐含的msb),那么尾数需要向右移动,并且指数递增。0
,则如果lsb是1
,则尾数仅递增。与情况2类似地处理尾数的溢出。由于尾数增量只有当它全部为1
时才会溢出,如果你没有携带尾数的msb(即,如果你已经放弃它,因为它会被丢弃在最终浮动表示),然后简单地通过将尾数设置为零并递增指数来固定尾数增量溢出的情况。
答案 1 :(得分:1)
我看到了你的问题并想起了我很久以前写过的一些浮点仿真代码。首先是浮点数的一个非常重要的建议。阅读"What Every Programmer Should know about Floating point",这是关于这个主题的非常好的完整指南。
至于我的代码,我挖了一遍并找到了它,但我必须警告你它很难看,因为这是个人项目(我的本科论文),所以没有得到适当的评论。此外,代码可能具有某些特性,因为它针对的是嵌入式系统(机器人)。解释项目并具有代码下载链接的页面链接为here。不介意网站,我恐怕不是网页设计师。)
这就是我在该项目中表示浮点的方式:
typedef struct
{
union{
struct {
unsigned long mantissa: 23;
unsigned long exponent: 8;
unsigned long sign: 1;
} float_parts; //the struct shares same memory space as the float
//allowing us to access its parts with the bitfields
float all;
};
}_float __attribute__((__packed__));
它使用bitfields我猜测的主题范围之外的解释,如果您想了解更多信息,请参阅链接。
我认为你会感兴趣的是这个功能。请注意,代码编写得不是很好,多年来我都没有看过它。另请注意,由于我只针对特定机器人的架构,因此代码不会检查字节序。但无论如何,我希望它对你有用。
_float intToFloat(int number)
{
int i;
//will hold the resulting float
_float result;
//depending on the number's sign determine the floating number's sign
if(number > 0)
result.float_parts.sign = 0;
else if(number < 0)
{
number *= -1; //since it would have been in twos complements
//being negative and all
result.float_parts.sign = 1;
}
else // 0 is kind of a special case
{
parseFloat(0.0,&result);
return result;
}
//get the individual bytes (not considering endiannes here, since it is for the robot only for now)
unsigned char* bytes= (unsigned char*)&number;
//we have to get the most significant bit of the int
for(i = 31; i >=0; i --)
{
if(bytes[i/8] & (0x01 << (i-((i/8)*8))))
break;
}
//and adding the bias, input it into the exponent of the float
//because the exponent says where the decimal (or binary) point is placed relative to the beginning of the mantissa
result.float_parts.exponent = i+127;
//now let's prepare for mantissa calculation
result.float_parts.mantissa = (bytes[2] << 16 | bytes[1] << 8 | bytes[0]);
//actual calculation of the mantissa
i= 0;
while(!(result.float_parts.mantissa & (0x01<<22)) && i<23) //the i is to make sure that
{ //for all zero mantissas we don't
result.float_parts.mantissa <<=1; //get infinite loop
i++;
}
result.float_parts.mantissa <<=1;
//finally we got the number
return result;
}
答案 2 :(得分:0)
感谢大家提供的非常有用的帮助!特别是,舍入规则特别有用!
我很高兴地说,在这个问题的回答以及所有光荣的人的帮助下,我成功地实现了这个功能。我的最终功能是:
unsigned float_i2f(int x) {
/* Apply a complex series of operations to make the cast. Rounding was achieved with the help of my post http://stackoverflow.com/questions/9288241/rules-for-explicit-int32-float32-casting. */
int sign, exponent, y;
int shift, shift_is_pos, shifted_x, deshifted_x, dropped;
int mantissa;
if (x==0) return 0;
sign = x<0 ? 0x80000000 : 0; //extract sign
x = sign ? -x : x; //absolute value, sorta
//Check how big the exponent needs to be to offset the necessary shift to the mantissa.
exponent = 0;
y = x;
while (y/=2) {
++exponent;
}
shift = exponent - 23; shift_is_pos = shift >= 0; //How much to shift x to get the mantissa, and whether that shift is left or right.
shifted_x = (shift_is_pos ? (x>>shift) : (x<<-shift)); //Shift x
deshifted_x = (shift_is_pos ? (shifted_x<<shift) : (shifted_x>>-shift)); //Unshift it (fills right with zeros)
dropped = x - deshifted_x; //Subtract the difference. This gives the rounding error.
mantissa = 0x007FFFFF & shifted_x; //Remove leading MSB (it is represented implicitly)
//It is only possible for bits to have been dropped if the shift was positive (right).
if (shift_is_pos) {
//We dropped some bits. Rounding may be necessary.
if ((0x01<<(shift-1))&dropped ) {
//The MSB of the dropped bits is 1. Rounding may be necessary.
//Kill the MSB of the dropped bits (taking into account hardware ignoring 32 bit shifts).
if (shift==1) dropped = 0;
else dropped <<= 33-shift;
if (dropped) {
//The remaining dropped bits have one or more bits set.
goto INC_MANTISSA;
}
//The remaining dropped bits are all 0
else if (mantissa&0x01) {
//LSB is 1
goto INC_MANTISSA;
}
}
}
//No rounding is necessary
goto CONTINUE;
//For incrementing the mantissa. Handles overflow by incrementing the exponent and setting the mantissa to 0.
INC_MANTISSA:
++mantissa;
if (mantissa&(0x00800000)) {
mantissa = 0;
++exponent;
}
//Resuming normal program flow.
CONTINUE:
exponent += 127; //Bias the exponent
return sign | (exponent<<23) | mantissa; //Or it all together and return.
}
它正确地解决了所有测试用例,虽然我确定它没有正确处理所有(例如,如果x是0x80000000,那么“绝对值”部分将返回0x80000000,因为溢出)。
再一次,我非常感谢你们所有人的帮助!
谢谢, 伊恩