以下转载自:https://www.cnblogs.com/silentNight/p/5274714.html
一个哥们在qq群里问了一个关于浮点数的程序,然后行了行浮点数的知识.竟然忘了,所有找了些文章.回忆回忆,理解理解
首先来聊天他的问题和让我无言以对的解决办法吧
"“十六进制转负数浮点数怎么转换啊”
然后我默默的写了一个下面的东东
#include "stdafx.h"
#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>
using namespace std;
float getFloat()
{
return (float)3.1423;
}
string FloatToHex ( float fNum )
{
int nInteger = (int)(fNum);
int nPower = 0;
while( fNum - nInteger > 1e-5 )
{
fNum *= 10;
nInteger = (int)(fNum);
nPower++;
}
std::cout<<nPower<<std::endl;
return "";
}
string FloatLength ( float fNum )
{
char cAryFloat[100] = {0};<br> sprint(cAryFloat, "%g", fNum);//从字符串cAryFloat中统计小数点后面有几位!!! %f 3.142300 %g 3.1423 %e 31423 + 1e-4
return "";
}
int _tmain(int argc, _TCHAR* argv[]) { FloatToHex(getFloat()); getchar(); return 0; } /* 在getFloat()函数中如果 return (float)3.14 那么最后打印的是2 在getFloat()函数中如果 return (float)3.1423 那么最后打印的是7 纠结了半天,猜测着可能你是返回3.1423但是你没办法控制float的精度,所有最后会打印7吧.
float最后可能是31422.9999999 打印的时候打印出了31423.但是强转int的话int仍然是32422.<br>所以又写了另外的函数string FloatLength ( float fNum ) 这个统计起来就没问题了..
下面的是他的代码:
int i = 0xbd600d1b;
float *f = (float *)&i;
printf("%f\n",*f);
float ff = -0.0547;
int *ii = (int *)&ff;
printf("%X\n",*ii);
反正也对吧…但是总有一种无言以对的感觉…
接下来就要进入正题,讲一讲浮点数在内存中的表示了.
首先是在c和c#中的浮点类型分两种 单精度float和双精度double. float或者double在内存中的组织都遵循IEEE的规范的,float遵从的是IEEE R32.24 ,而double 遵从的是R64.53
然后呢对于一个存储在内存的浮点数都包括3部分 : 符号位 指数位 小数部分 (而float和double的区别就是各部分占得位数可能不同)
恩.以上基本就可以把float和double的东西搞得差不多了…
大牛还弄了个例子来说明把一个float赋值给double的话 double的值未必等于float…我们来看看吧
猜测一下下面的程序的输出结果是什么:
float f = 2.2f;
double d = (double)f;
Console.WriteLine(d.ToString("0.0000000000000"));
f = 2.25f;
d = (double)f;
Console.WriteLine(d.ToString("0.0000000000000"));
可能输出的结果让大家疑惑不解,单精度的2.2转换为双精度后,精确到小数点后13位后变为了2.2000000476837,而单精度的2.25转换为双精度后,变为了2.2500000000000,为何2.2在转换后的数值更改了而2.25却没有更改呢?很奇怪吧?
首先我们看看2.25的单精度存储方式,很简单 0 1000 0001 001 0000 0000 0000 0000 0000,而2.25的双精度表示为:0 100 0000 0001 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000,这样2.25在进行强制转换的时候,数值是不会变的,
ps: 先插入一个怎么把十进制小数转换成二进制的小数的方法---->
但是这样存储方式,换算成十进制的值,却不会是2.2的,应为十进制在转换为二进制的时候可能会不准确,如2.2,而double类型的数据也存在同样的问题,所以在浮点数表示中会产生些许的误差,在单精度转换为双精度的时候,也会存在误差的问题,对于能够用二进制表示的十进制数据,如2.25,这个误差就会不存在,所以会出现上面比较奇怪的输出结果。
恩,学习完了.附上原作者的链接 http://www.cnblogs.com/jillzhang/archive/2007/06/24/793901.html
https://blog.csdn.net/nesll/article/details/52302520
https://blog.csdn.net/j864458340/article/details/78435230
https://blog.csdn.net/ganxingming/article/details/1449526
以下转载自:
https://blog.csdn.net/gunqu_d/article/details/76401283
整型与浮点型数据在计算机内存中的存储
1、类型的归类
整型家族:char、unsigned char、signed char //对于char来说,标准里并不默认其为有符号还是无符号的,这个结果取决于编译器。在计算机中,我们实际上可以用整形数据来模拟定点数运算。
short ( signed short [int] )、unsigned short [int]
int ( signed int )、unsigned int
long ( signed long [int] )、unsigned long [int]
浮点数家族:
float
double
构造类型:数组类型
结构体类型 struct
枚举类型 enum
联合类型 union
指针类型
空类型
2、整型在内存中的存储
1)整型在内存中以补码的形式存储,浮点数则没有补码之说,它只需要规定指数与尾数。
原因有三:其一,使用补码可以将符号位和数值域统一处理;其二,加法和减法可以统一处理(cpu只有加法器);其三,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
2)数据存储的大小端模式
小端字节序存储:数据的低字节存到低地址处,高字节存到高地址处。
大端字节序存储:数据的低字节存到高地址处,高字节存到低地址处。
存在不同字节序存储模式的原因:因为在计算机系统中是以字节做单位的,每个地址单元对应一个字节,一个字节有8个比特位。不过在c语言中,除了具有一个字节(8b)的char类型外,还有2个字节(16b)的short型、4个字节(32b)的int型等,而且对于位数大于8的处理器,由于寄存器宽度大于一个字节,所以就必然存在如何安排多个字节的问题。这就是小端存储模式与大端存储模式出现的原因。
例如对于一个short类型的x数据,在内存中的地址为0x0010,其值为0x1122,那么0x11是x的高字节,0x22是x的低字节。对于大端存储模式,0x11放在低地址的0x0010中,0x22放在高地址的0x0011中。对于小端存储模式则相反。
关于大端小端字节序:
https://blog.csdn.net/m0_37949304/article/details/75576631
3、浮点数在内存中的存储
1)根据国际标准IEEE754,任意一个二进制浮点数V可以表示为下面的形式:
(-1)^S*M*2^E
(-1)^S表示符号位,当S=0时,V为正数;当S=1时,V为负数
M表示有效数字,大于等于1,小于2
2^E表示指数位
2)对于32位的浮点数和64位浮点数的不同规定
对于32位的浮点数,最高1位是符号位S,接着是8位的指数E,剩下的23位是有效数字M。
对于64位的浮点数,最高1位是符号位S,接着的11位是指数E,剩下的52位是有效数字M。
3)几点注意
第一,对于有效数字M,因为其值大于等于1而小于2,所以在计算机中,为了能够利用23位(52位)表示更多的数据,IEEE754规定保存M时默认这个数的第一位为1,所以只保存后面的部分(小数点后的位)。然后等到读取此数的时候,再把第一位的1加上去。
第二,对于指数E,为了能够表示负数的指数,IEEE754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数为127;对于11位的E,这个中间数是1023。
第三,当E全为0时,读取该数字时有效数字M不再加上第一位的1,因为这是一个无限接近与0的数字,表示正负0;当E全为1的时候,若M全为0,则表示一个正负无穷大的数。
以下转载自:
https://blog.csdn.net/xiaofeng119/article/details/6176446
再看个论坛的例子吧,其实这个问题比较傻,浮点数根部不谈补码,而小数却有定点数表示,只有整形数才有补码,定点数说补码的概念都过于勉强:
小数的补码怎么求?正负小数的补码怎样求?和整数的规律一样吗,求举例
https://bbs.csdn.net/topics/391841677?page=1
看下网友回答吧。。
网友1:
没见过,这种实现,定点小数?
网友2:
浮点数在内存中是用符号位、指数位、尾数部分组成的,和整数在内存中的模型不同的,具体参看:http://www.cnblogs.com/xkfz007/archive/2012/02/27/2370357.html
网友3:
浮点数,不采用补码表示
整数中,有符号数,用的是补码数表示
如果采用定点数,表示小数,那就和整数相当了。
网友4:
类似 —1010101.1010101
小数部分怎么求补码
网友5:
定点数 小数其实就是整数
只是输入输出按照小数输入输出而已
必要时,可能需要四舍五入处理
整型数据和字符串数据在内存中的存放
原来对于数据在内存中的存放有点模糊,昨天翻出来大学时候的c语言课本,仔细研究了一下。数据在计算机中都会以二进制的形式进行存放,其中对于整型数据在内存中的存放都是以补码的形式存放的,正数的补码是本身,负数的补码是其绝对值加1.而对于字符型数据会把字符所对应的ASCII码存入到内存中,下面以int和char类型的为例:
整型数据在内存中的存放形式
如果定义了一个整型变量i:
int i;
i=10;
数值是以补码表示的:
n 正数的补码和原码相同;
n 负数的补码:将该数的绝对值的二进制形式按位取反再加1。
例如:
求-10的补码:
10的原码:
由此可知,左面的第一位是表示符号的。
整型数据在内存中所占的位数(如下图所示)
字符数据在内存中的存放形式
如果定义了一个字符串变量i:
char a;
a = ‘x’
x的ASCII码为120,转换成二进制为1111000
在c语言中字符数据占一个字节8位
最后附上一个例子吧:
输出数字在内存中呈现的二进制数值
以下例子用于输出一个数字在内存中呈现的二进制数值。
其中有一些知识点需要注意:
1.sizeof(num)
用于获得num
在内存中占用的字节数
2.<<
是二进制位左移操作
num<<1 --相当于-- num*2
num<<2 --相当于-- num*2*2
num<<n --相当于-- num*2^n(2的n次方)
3.>>
是二进制位右移操作
num>>1 --相当于-- num/2
num>>2 --相当于-- num/(2*2)
num>>n --相当于-- num/2^n(2的n次方)
4.&
是二进制位与操作,任何数与1位与都可以得到这个数二进制的最低位
8 & 1 --相当于-- 0b1000 & 0b0001
1000
0001
------
0000
因此计算结果为 0 --相当于-- 0b0000(相较于原文有改动)
以下为例子:
void printBinary(int num);
int main()
{
int q;
do
{
int num;
printf("输入一个数\n");
scanf("%d",&num);
printBinary(num);
printf("按任意键继续,0退出!\n");
scanf("%d",&q);
}
while(q);
}
//打印数值对应的二进制
void printBinary(int num)
{
// sizeof(num)<<3 --相当于-- sizeof(num)*8
int cnt=(sizeof(num)<<3)-1;
for(int i=cnt;i>=0;i--)
{
// num>>i --相当于-- num/2^i(2的i次方)
//此例中的位移只是为了进行位与运算(&)得到最低位的值
int temp=num>>i;
int b=temp&1;
printf("%d",b);
if(i%4==0)
{
printf(" ");
}
}
printf("\n");
}
输出结果
输入一个数
1
0000 0000 0000 0000 0000 0000 0000 0001
按任意键继续,0退出!
1
输入一个数
10
0000 0000 0000 0000 0000 0000 0000 1010
按任意键继续,0退出!
0
其实关于上面这个问题,还有一种算法思路,可能会更加高效。
参考自:https://blog.csdn.net/linfeng24/article/details/34209271
今天在华为OJ上遇到这么一个题目,很简单,但是却总是得不到最好的成绩记录。因此比较了自己的程序、思路与别人的异同,发现还是有很大区别的,遂记录如下。
题目——
输入一个int型整数,求该整数的二进制在内存中有多少个1。例如输入10,由于其二进制表示为1010,有两个1,因此输出2。
思路1
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int m = scanner.nextInt();
int num = 0;
while(m>0){
m&=(m-1);
num++;
}
}
这是最常规的思路。直接利用移位去算,始终判断最后一位是不是1,然后计数。这也是我一开始所所想到的,但是,这种写法只得到了60分。
思路2
private static int count_ones(int a) {
a = (a & 0x55555555) + ((a >> 1) & 0x55555555);
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
a = (a & 0x0f0f0f0f) + ((a >> 4) & 0x0f0f0f0f);
a = (a & 0x00ff00ff) + ((a >> 8) & 0x00ff00ff);
a = (a & 0x0000ffff) + ((a >> 16) & 0x0000ffff);
return a;
}
这是我看别人拿了满分的答案。这种思路比较难想到。一开始我怎么都想不明白他是怎么想出来的。直到翻看书籍,碰巧在《编程之美》上有记载这个题目,并且给出了五种解法。其中有两种就是以上两种。
这道题的本质相当于求二进制数的 Hamming 权重,或者说是该二进制数与 0 的 Hamming 距离,这两个概念在信息论和编码理论中是相当有名的。在二进制的情况下,它们也经常被叫做 population count 或者 popcount 问题,比如 gcc 中就提供了一个内建函数:int __builtin_popcount (unsigned int x) 输出整型数二进制中 1 的个数。但是 GCC 的 __builtin_popcount 的实现主要是基于查表法做的,跟编程之美中解法 5 是一样的。Wikipedia 上的解法是基于分治法来做的,构造非常巧妙,通过有限次简单地算术运算就能求得结果,特别适合那些受存储空间限制的算法中使用。
此外,在这个博客里也有对编程之美里五种解法的详细分析,值得一读。http://blog.csdn.net/shijiemazhenda/article/details/6785674
再看两个例子:
数字字符转换成数字
输入两个数字字符,将它们转换成十进制后输出
#include <stdio.h>
int main()
{
char ch;
int i,data;
data=0;
for(i=0;i<2;i++)
{
while(1)
{
ch=getchar();
if(ch>='0'&&ch<='9')
{
break;
}
}
data=data*10+ch-'0';
}
printf("data=%d\n",data);
}
十进制转换
data=data*10+ch-‘0’;
删除一个字符串中的所有数字字符
#include <stdio.h>
void delnum(char *s)
{
int i,j;
for(i=0,j=0;s[i]!='\0' ;i++)//1
if(s[i]<'0'||s[i]>'9')//2为什么?
{
s[j]=s[i];//3
j++;
}
s[j]='\0';
}
int main ()
{
char item[1001];
printf("input a string:\n");
gets(item);
delnum(item);//4
printf("\n%s",item);
}