浮点数移位操作和类型转换问题
- 今天遇到了一些坑,之前自己用移位后的int结果不对;然后改为原始的double就可以;今天硬着头皮才知道自己犯了很多错误!
之前的代码:Mat[]为double类型
y0 = (RegionInfoList[i].minx*Mat[3] - RegionInfoList[i].miny*Mat[0] - (Mat[2] * Mat[3] - Mat[0] * Mat[5])) / (Mat[1] * Mat[3] - Mat[0] * Mat[4]);
x0 = (RegionInfoList[i].minx*Mat[4] - RegionInfoList[i].miny*Mat[1] - (Mat[2] * Mat[4] - Mat[1] * Mat[5])) / (Mat[0] * Mat[4] - Mat[1] * Mat[3]);
y1 = (RegionInfoList[i].maxx*Mat[3] - RegionInfoList[i].maxy*Mat[0] - (Mat[2] * Mat[3] - Mat[0] * Mat[5])) / (Mat[1] * Mat[3] - Mat[0] * Mat[4]);
x1 = (RegionInfoList[i].maxx*Mat[4] - RegionInfoList[i].maxy*Mat[1] - (Mat[2] * Mat[4] - Mat[1] * Mat[5])) / (Mat[0] * Mat[4] - Mat[1] * Mat[3]);
需要Mat[]改为左移16位的int类型;先想直接将Mat[]的每个元素(Mat[i]>>16)就行了,但是这样做跟直接用double类型的没有区别。而且这样做有错误!最多到1.0
int a = 66519;
double b = a >> 16;
printf("移位结果:%lf\n", b); //输出1.000000
然后查C语言浮点数不能进行移位操作
- 有符号整数的移位操作
- 浮点数的移位操作
- 移位操作VS使用联合体
无符号整数的移位操作
C语言中,有符号数据的移位操作和无符号的移位操作不同,无符号的移位操作为逻辑移位,即高位舍弃,低位补0;高位补0,低位舍弃。有符号的整数类型的移位操作为算数移位,也就是最高位保持不变,负数高位移位后还是保持“1”,正数移位后还是保持“0”,而其他位和逻辑移位一样。
浮点数的移位操作
C语言不支持浮点数的移位操作,浮点数的存储和整型数的存储不同,并不是直接将数值表示成二进制形式存储,存储的方式在下表呈现,具体的在这里不多说,浮点数直接移位后基本没有什么意义,但是有时候还是需要进行移位操作,比如使用串口/IIC/CAN等通信时,需要将数据一字节一字节的发送,对于浮点数来说,就需要将浮点数分解成字节,这样常用的有两种方法,一是利用联合体的特点将浮点数分解,二是利用移位操作,移位操作需要一些技巧。
浮点数不能直接使用移位操作,整型可以,所以将浮点数转化为整型数进行移位是可行的,但是这里的类型转化不是类似(int)a这样的强制类型转换,而是将存储整型的数据内存的解释规则转化为整型,比如进行如下操作:
float a;
uint32_t *data = (uint32_t *)&a;
经过这样的操作后,保存数据a的内存,就可以使用data或者(uint32_t )&a解释成整型,并且对data或者(uint32_t )&a进行移位操作把浮点数分解为单个字节并且不改变每一位的值。
移位操作VS使用联合体
下面的两段代码都可以将浮点数分解为单字节并进行操作,比较来看,在将数据(并不限制为浮点数)分解为字节倍数的类型时,联合体的操作更加简单易懂,但是移位操作没有这一限制,比使用联合体灵活,但也相对复杂,比较来看,各有千秋。
//浮点数的移位操作
#include "stdint.h"
#include "stdio.h"
int main(void)
{
double val = 48646.1123;
double Result = 0;
uint8_t data2[8];
for (int i = 0;i < 8;i++) {
data2[i] = ((*(uint64_t *)&val) >> (8 * i)) & 0xff;
}
for (int i = 0;i < 8;i++) {
(*(uint64_t *)&Result) = (*(uint64_t *)&Result) << 8 | data2[7 - i];
}
printf("%f", Result);
system("pause");
}
//使用联合体达到移位操作效果
#include "stdio.h"
#include "stdint.h"
#include "string.h"
int main(void)
{
union
{
uint8_t data[8];
double val;
}test, test2;
double x = 5.1;
double re;
int i;
test.val = x;
for (i = 0; i < 8; i++)
{
test2.data[i] = test.data[i];
}
re = test2.val;
printf("%f", re);
}
** 浮点数移位转化为字节操作**
最后处理方法
本身等式就是上下除的形式,然后可以抵消;遇到的坑就是(Mat[2] * (long long)Mat[3])这些地方都要用强制类型转换。然后强制类型转换需要在运算前进行,计算后转换是没有效果的。
//注意溢出处理
y0 = (((long long)(RegionInfoList[i].minx*Mat[3] - RegionInfoList[i].miny*Mat[0]) << 16) - ((Mat[2] * (long long)Mat[3]) - (Mat[0] * (long long)Mat[5]))) / ((Mat[1] * (long long)Mat[3]) - (Mat[0] * (long long)Mat[4]));
x0 = (((long long)(RegionInfoList[i].minx*Mat[4] - RegionInfoList[i].miny*Mat[1]) << 16) - ((Mat[2] * (long long)Mat[4]) - (Mat[1] * (long long)Mat[5]))) / ((Mat[0] * (long long)Mat[4]) - (Mat[1] * (long long)Mat[3]));
y1 = (((long long)(RegionInfoList[i].maxx*Mat[3] - RegionInfoList[i].maxy*Mat[0]) << 16) - ((Mat[2] * (long long)Mat[3]) - (Mat[0] * (long long)Mat[5]))) / ((Mat[1] * (long long)Mat[3]) - (Mat[0] * (long long)Mat[4]));
x1 = (((long long)(RegionInfoList[i].maxx*Mat[4] - RegionInfoList[i].maxy*Mat[1]) << 16) - ((Mat[2] * (long long)Mat[4]) - (Mat[1] * (long long)Mat[5]))) / ((Mat[0] * (long long)Mat[4]) - (Mat[1] * (long long)Mat[3]));
最后看看各种类型的范围:
速查表:
char -128 ~ +127 (1 Byte)
short -32767 ~ + 32768 (2 Bytes)
unsigned short 0 ~ 65535 (2 Bytes)
int -2147483648 ~ +2147483647 (4 Bytes)
unsigned int 0 ~ 4294967295 (4 Bytes)
long == int
long long -9223372036854775808 ~ +9223372036854775807 (8 Bytes)
double 1.7 * 10^308 (8 Bytes)
unsigned int 0~4294967295
long long的最大值:9223372036854775807
long long的最小值:-9223372036854775808
unsigned long long的最大值:18446744073709551615
__int64的最大值:9223372036854775807
__int64的最小值:-9223372036854775808
unsigned __int64的最大值:18446744073709551615
- 实验一下支持Latex公式插入:
\(x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}\)