手工算浮点数(float or double)到16进制表示的科学记数法(4bytes or 8bytes)时,因为不能马上确定自己算的对不对.
正好刷题时,有一道题,要求将浮点值转成科学记数法. 就直接改成了一个能验证科学记数法是否计算正确的辅助工具~
源码
/// @file exam_2_1.c
/** @brief
1. 写出58.25的16进制
* 这个测试程序是用来确定浮点数在内存中的地址, 当我们手工算完float值后,
可以WinHex将转换好的16进制浮点数填入内存,
然后继续测试程序, 显示我们自己填写的float和double, 看看手算的16进制浮点数是否正确
*/
#include <stdlib.h>
#include <stdio.h>
/// 用CL编译, 不知道为啥不能使用 bool 类型, 自己定义一个
/// error C2065: 'bool' : undeclared identifier
/// bool 不是C语言内建的数据类型么?
#define LS_DEFINE
#ifdef LS_DEFINE
typedef char bool;
#define true 1
#define false 0
#endif // #ifdef LS_DEFINE
#ifndef MAX_PATH
#define MAX_PATH 260
#endif // #ifndef MAX_PATH
void clearScreen(); ///< 清屏
void procTask();
void print_memory_addr_content(unsigned char* pcAddr, int iBytes);
void fill_memory(unsigned char* pcAddr, unsigned char uContent, int iBytes);
int main(int agrc, char** argv)
{
clearScreen();
procTask();
return 0;
}
void procTask()
{
/// 1. 写出58.25的16进制
/// 思路
/// 我们定义一个float fObj,一个double dblObj
/// 让程序打印出fObj和dblObj的内存地址
/// 先到了我们暂停的点(等待键盘输入)
/// 分别算出16进制浮点数后,用WinHex添入fObj的内存地址中和dblObj的内存地址中
/// 按下让程序继续跑
/// 让程序打印出fObj和dblObj在内存中的字节内容和float值和double值
/// 这样就可以验证我们手工计算的float16进制值和double16进制值是否正确
unsigned int uAddrBegin = 0x55AA; ///< 作个内存标记, 可以用来确认winHex打开的地址, 确实是我们的程序
float fObj = .0f;
double dblObj = 0;
unsigned int uAddrEnd = 0xAA55; ///< 作个内存标记, 可以用来确认winHex打开的地址, 确实是我们的程序
printf("sizeof(unsigned int) = %d, &uAddrBegin = 0x%X\n", sizeof(unsigned int), &uAddrBegin);
printf("sizeof(float) = %d, &fObj = 0x%X\n", sizeof(float), &fObj);
printf("sizeof(double) = %d, &dblObj = 0x%X\n", sizeof(double), &dblObj);
printf("sizeof(unsigned int) = %d, &uAddrEnd = 0x%X\n", sizeof(unsigned int), &uAddrEnd);
/// 填充fObj和dblObj到特定的预设值, 方便我们在内存中查找确认要操作的目标
fill_memory((unsigned char*)&fObj + 0, 0xf0, 1);
fill_memory((unsigned char*)&fObj + 1, 0xf1, 1);
fill_memory((unsigned char*)&fObj + 2, 0xf2, 1);
fill_memory((unsigned char*)&fObj + 3, 0xf3, 1);
fill_memory((unsigned char*)&dblObj + 0, 0xd0, 1);
fill_memory((unsigned char*)&dblObj + 1, 0xd1, 1);
fill_memory((unsigned char*)&dblObj + 2, 0xd2, 1);
fill_memory((unsigned char*)&dblObj + 3, 0xd3, 1);
fill_memory((unsigned char*)&dblObj + 4, 0xd4, 1);
fill_memory((unsigned char*)&dblObj + 5, 0xd5, 1);
fill_memory((unsigned char*)&dblObj + 6, 0xd6, 1);
fill_memory((unsigned char*)&dblObj + 7, 0xd7, 1);
print_memory_addr_content((unsigned char*)&uAddrBegin, 4);
print_memory_addr_content((unsigned char*)&fObj, 4);
print_memory_addr_content((unsigned char*)&dblObj, 8);
print_memory_addr_content((unsigned char*)&uAddrEnd, 4);
printf("please use winHex fill value to &fObj and &dblObj\n");
printf("when fill data over, press any key to continue\n");
/** run result
sizeof(unsigned int) = 4, &uAddrBegin = 0x12FF68
sizeof(float) = 4, &fObj = 0x12FF64
sizeof(double) = 8, &dblObj = 0x12FF70
sizeof(unsigned int) = 4, &uAddrEnd = 0x12FF6C
memory addr[12FF68] : AA 55 00 00
memory addr[12FF64] : F0 F1 F2 F3
memory addr[12FF70] : D0 D1 D2 D3 D4 D5 D6 D7
memory addr[12FF6C] : 55 AA 00 00
please use winHex fill value to &fObj and &dblObj
when fill data over, press any key to continue
*/
/** 分析
可以看到变量内存布局和我们定义的顺序并不相同, 由于内存字节对齐的原因
我们定义的变量顺序为 uAddrBegin => fObj => dblObj => uAddrEnd
程序编译优化后,改成了
0x12FF6C uAddrBegin (占用4字节)
0x12FF70 fObj (占用4字节)
0x12FF74 uAddrEnd (占用4字节)
0x12FF78 dblObj (占用8字节)
*/
/// 在这里计算 fObj = 58.25f 和 dblObj = 58.25 的16进制科学记数法的值
/**
================================================================================
16进制参考值
================================================================================
0 = 0000 1 = 0001 2 = 0010 3 = 0011 4 = 0100 5 = 0101 6 = 0110 7 = 0111
8 = 1000 9 = 1001 A = 1010 B = 1011 C = 1100 D = 1101 E = 1110 F = 1111
================================================================================
float 值 58.25f 转 科学记数法
================================================================================
58.25f = 58 + 0.25f
* 整数部分 58 转 二进制
58 / 2 = 29 left 0
29 / 2 = 14 left 1
14 / 2 = 7 left 0
7 / 2 = 3 left 1
3 / 2 = 1 left 1
从最后一次的除法的商依次念到第一次除法的余数 111010
so 58 = 111010B
* 小数部分 0.25f 转二进制
0.25 * 2 = 0.5 up 0
0.5 * 2 = 0.0 up 1
从第一次的进位念到最后一次的进位 01
so 0.25f = .01B
可得
58.25f = 58 + 0.25f
= 111010.01B * 10B^0
= 1.1101001B * 10B^5
float的科学记数法 S(符号位) = 1, E(指数位) = 8, D(数据位) = 23
S = 0 (正数)
E = 127 + 5 = 132D = 10000100B
D = 1101001000 0000000000 000B
科学记数法值 = SED
= 0 10000100 1101001000 0000000000 000
= 0100,0010,0110,1001,0000,0000,0000,0000
= 4 2 6 9 0 0 0 0
= 42690000H
================================================================================
double 值 58.25f 转 科学记数法
================================================================================
58.25f = 58 + 0.25f
* 整数部分 58 转 二进制
58 / 2 = 29 left 0
29 / 2 = 14 left 1
14 / 2 = 7 left 0
7 / 2 = 3 left 1
3 / 2 = 1 left 1
从最后一次的除法的商依次念到第一次除法的余数 111010
so 58 = 111010B
* 小数部分 0.25f 转二进制
0.25 * 2 = 0.5 up 0
0.5 * 2 = 0.0 up 1
从第一次的进位念到最后一次的进位 01
so 0.25f = .01B
可得
58.25f = 58 + 0.25f
= 111010.01B
= 1.1101001B * 10B^5
float的科学记数法 S(符号位) = 1, E(指数位) = 11, D(数据位) = 52
S = 0 (正数)
E = 1023 + 5 = 1028D = 10000000100B
D = 1101001000 0000000000 0000000000 0000000000 0000000000 00B
科学记数法值 = SED
= 0 10000000100 1101001000 0000000000 0000000000 0000000000 0000000000 00
= 0100,0000,0100,1101,0010,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000,0000
= 4 0 4 D 2 0 0 0 0 0 0 0 0 0 0 0
= 404D200000000000H
================================================================================
16进制参考值
================================================================================
0 = 0000 1 = 0001 2 = 0010 3 = 0011 4 = 0100 5 = 0101 6 = 0110 7 = 0111
8 = 1000 9 = 1001 A = 1010 B = 1011 C = 1100 D = 1101 E = 1110 F = 1111
*/
getchar();
/// 我们上边已经算出了16进制科学记数法的值, 我们自己写进内存, 和用WinHex手工写是一样的~
// 58.25f = 42690000H
fill_memory((unsigned char*)&fObj + 0, 0x00, 1);
fill_memory((unsigned char*)&fObj + 1, 0x00, 1);
fill_memory((unsigned char*)&fObj + 2, 0x69, 1);
fill_memory((unsigned char*)&fObj + 3, 0x42, 1);
// 58.25f = 404D200000000000H
fill_memory((unsigned char*)&dblObj + 0, 0x00, 1);
fill_memory((unsigned char*)&dblObj + 1, 0x00, 1);
fill_memory((unsigned char*)&dblObj + 2, 0x00, 1);
fill_memory((unsigned char*)&dblObj + 3, 0x00, 1);
fill_memory((unsigned char*)&dblObj + 4, 0x00, 1);
fill_memory((unsigned char*)&dblObj + 5, 0x20, 1);
fill_memory((unsigned char*)&dblObj + 6, 0x4d, 1);
fill_memory((unsigned char*)&dblObj + 7, 0x40, 1);
/// 打印出我们修改后的值
printf("after you modify, memory below\n");
print_memory_addr_content((unsigned char*)&uAddrBegin, 4);
print_memory_addr_content((unsigned char*)&fObj, 4);
print_memory_addr_content((unsigned char*)&dblObj, 8);
print_memory_addr_content((unsigned char*)&uAddrEnd, 4);
printf("fObj = %f\n", fObj);
printf("dblObj = %f\n", dblObj);
/// 程序结束前,停一下, 让我们看看修改的效果
printf("END, press any key to quit\n");
getchar();
}
void print_memory_addr_content(unsigned char* pcAddr, int iBytes)
{
int iIndex = 0;
printf("memory addr[%X] : ", pcAddr);
for (iIndex = 0; iIndex < iBytes; iIndex++)
{
printf("%02X ", *(pcAddr + iIndex));
}
printf("\n");
}
void fill_memory(unsigned char* pcAddr, unsigned char uContent, int iBytes)
{
int iIndex = 0;
for (iIndex = 0; iIndex < iBytes; iIndex++)
{
*(pcAddr + iIndex) = uContent;
}
}
void clearScreen()
{
system("cls");
}
编译脚本
d:\
cd D:\ls\lesson\2015_1023\exam_2_1
del *.exe
del *.obj
"C:\Program Files\Microsoft Visual Studio\VC98\Bin\cl" /c /W3 /WX /O2 exam_2_1.c
"C:\Program Files\Microsoft Visual Studio\VC98\Bin\link" exam_2_1.obj
call exam_2_1.exe
pause
运行效果