鱼C论坛

 找回密码
 立即注册
查看: 1271|回复: 6

[已解决]float 的取值范围问题

[复制链接]
发表于 2022-3-2 20:55:50 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1.试问取值范围是只取指数部分吗,小数位不理会吗
2.请问能详细解说取值范围数字如何得来,指数位有8位,所以0和1排列组合2^8=256,到这后面我就不懂了,还有最后为什么是-2^128 ~ +2^128,也即-3.40E+38 ~ +3.40E+38.. 那个2怎么来的,能詳細介紹取值範圍的算式嗎,感謝
最佳答案
2022-3-4 19:30:08
看下面的内容之前,你要先看过这篇文章或者类似的文章
浮点数在计算机中的存储方式:https://www.cnblogs.com/wuyuan2011woaini/p/4105765.html

float类型的浮点数的存储格式使用的是 IEEE 的 R32.24 标准
就是 1位符号位,8位指数位,23位尾数位

那么如何得到最大值呢?
在计算机中不是1就是0
1 大于 0
全 1 就是最大了
那就这样
0 11111111 11111111111111111111111        # 最大
符号位是0,表示这是一个正数

“元数据+127:大概是指“指数”从00000000开始(表示-127)至11111111(表示+128)”

就是说指数部分的这个 11111111,表示的其实是 128


“任何一个数的科学计数法表示都为1. xxx * 2n ,尾数部分就可以表示为xxxx,由于第一位都是1嘛,干嘛还要表示呀?所以将小数点前面的1省略。”

所以上面那个值就可以写成这样
1.11111111111111111111111 * 2^128   # 小数点后面有23个1

这个写法可能不方便理解,不方便之后的计算,换成下面这样的写法,不使用科学记数法
# 24个1,105个0
111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

这个就是最大值,但是这是二进制形式的,要转换成十进制才更方便理解
怎么转呢?这就是另外一个问题了
二进制转十进制,自己百度

但是嘛,就是你会转换,这个规模的数字也不应该人工进行转换
现在用我们的python来转换
$ cat main.py
#!/usr/bin/env python
#coding=utf-8

#number = '1110'
number = '111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
number_len = len(number)
result = 0
for i in range(number_len):
    if number[number_len - i - 1] == '1':
        result += 2**i

print(result)
print(f'{result:e}')
$ ./main.py
680564693277057719623408366969033850880
6.805647e+38
$

这么一长串的二进制转成十进制是 680564693277057719623408366969033850880
科学记数法表示成 6.805647e+38

这个明显不是最大值,因为你题目中说最大值是 3.40E+38 (-3.40E+38 ~ +3.40E+38)

问题的原因是浮点数中有两个特殊值 INF 和 NAN
确实是两个,IND其实就是NAN,他们是一回事,不同的环境可能会选NAN,也可能选IND
不管怎么说,他们是完全一样的,都是一回事
INF IND NAN 参考下面的文章

https://www.cnblogs.com/Malphite/p/12068991.html
https://www.thinbug.com/q/15288406


阶码全1,尾数全0表示无穷大INF。例如1.0/0.0

阶码全1,尾数非全0的表示无效数NaN。例如:求负数的平方根,例如0.0/0.0。


也就是说 指数部分不能全1,全1已经给这两个特殊值占用了
那就让 11111111 减 1,就是去掉这两个特殊值后的最大值

0 11111110 11111111111111111111111        # 最大

没错,这里是127,之前是128
1.11111111111111111111111 * 2^127   # 小数点后面有23个1

# 24个1,104个0
111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

现在,再让咱们的python算一下这个十进制是多少
$ cat main.py
#!/usr/bin/env python
#coding=utf-8

#number = '1110'
number = '11111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
number_len = len(number)
result = 0
for i in range(number_len):
    if number[number_len - i - 1] == '1':
        result += 2**i

print(result)
print(f'{result:e}')
$ ./main.py
340282346638528859811704183484516925440
3.402823e+38
$

这一次算出的结果是 340282346638528859811704183484516925440
科学记数法表示成 3.402823e+38

这个就是float的最大值
最小值?
简单,前面加上一个负号就是了

所以float的取值范围就是
-340282346638528859811704183484516925440 ~ 340282346638528859811704183484516925440
科学记数法就表示成
-3.402823e+38 ~ 3.402823e+38

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-3-2 21:21:34 From FishC Mobile | 显示全部楼层
计算机内核是玩二进制的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-3-2 22:55:47 | 显示全部楼层
数学劝退
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2022-3-3 12:31:03 From FishC Mobile | 显示全部楼层
有大佬能夠解答嗎
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-3-4 11:15:26 | 显示全部楼层
浮点数在机内用指数型式表示,分解为:数符,尾数,指数符,指数四部分。
数符占 1 位二进制,表示数的正负。
指数符占 1 位二进制,表示指数的正负。
尾数表示浮点数有效数字,0.xxxxxxx, 但不存开头的 0 和点。
指数存指数的有效数字。
指数占多少位,尾数占多少位,由计算机系统决定
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-3-4 19:30:08 | 显示全部楼层    本楼为最佳答案   
看下面的内容之前,你要先看过这篇文章或者类似的文章
浮点数在计算机中的存储方式:https://www.cnblogs.com/wuyuan2011woaini/p/4105765.html

float类型的浮点数的存储格式使用的是 IEEE 的 R32.24 标准
就是 1位符号位,8位指数位,23位尾数位

那么如何得到最大值呢?
在计算机中不是1就是0
1 大于 0
全 1 就是最大了
那就这样
0 11111111 11111111111111111111111        # 最大
符号位是0,表示这是一个正数

“元数据+127:大概是指“指数”从00000000开始(表示-127)至11111111(表示+128)”

就是说指数部分的这个 11111111,表示的其实是 128


“任何一个数的科学计数法表示都为1. xxx * 2n ,尾数部分就可以表示为xxxx,由于第一位都是1嘛,干嘛还要表示呀?所以将小数点前面的1省略。”

所以上面那个值就可以写成这样
1.11111111111111111111111 * 2^128   # 小数点后面有23个1

这个写法可能不方便理解,不方便之后的计算,换成下面这样的写法,不使用科学记数法
# 24个1,105个0
111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

这个就是最大值,但是这是二进制形式的,要转换成十进制才更方便理解
怎么转呢?这就是另外一个问题了
二进制转十进制,自己百度

但是嘛,就是你会转换,这个规模的数字也不应该人工进行转换
现在用我们的python来转换
$ cat main.py
#!/usr/bin/env python
#coding=utf-8

#number = '1110'
number = '111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
number_len = len(number)
result = 0
for i in range(number_len):
    if number[number_len - i - 1] == '1':
        result += 2**i

print(result)
print(f'{result:e}')
$ ./main.py
680564693277057719623408366969033850880
6.805647e+38
$

这么一长串的二进制转成十进制是 680564693277057719623408366969033850880
科学记数法表示成 6.805647e+38

这个明显不是最大值,因为你题目中说最大值是 3.40E+38 (-3.40E+38 ~ +3.40E+38)

问题的原因是浮点数中有两个特殊值 INF 和 NAN
确实是两个,IND其实就是NAN,他们是一回事,不同的环境可能会选NAN,也可能选IND
不管怎么说,他们是完全一样的,都是一回事
INF IND NAN 参考下面的文章

https://www.cnblogs.com/Malphite/p/12068991.html
https://www.thinbug.com/q/15288406


阶码全1,尾数全0表示无穷大INF。例如1.0/0.0

阶码全1,尾数非全0的表示无效数NaN。例如:求负数的平方根,例如0.0/0.0。


也就是说 指数部分不能全1,全1已经给这两个特殊值占用了
那就让 11111111 减 1,就是去掉这两个特殊值后的最大值

0 11111110 11111111111111111111111        # 最大

没错,这里是127,之前是128
1.11111111111111111111111 * 2^127   # 小数点后面有23个1

# 24个1,104个0
111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

现在,再让咱们的python算一下这个十进制是多少
$ cat main.py
#!/usr/bin/env python
#coding=utf-8

#number = '1110'
number = '11111111111111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
number_len = len(number)
result = 0
for i in range(number_len):
    if number[number_len - i - 1] == '1':
        result += 2**i

print(result)
print(f'{result:e}')
$ ./main.py
340282346638528859811704183484516925440
3.402823e+38
$

这一次算出的结果是 340282346638528859811704183484516925440
科学记数法表示成 3.402823e+38

这个就是float的最大值
最小值?
简单,前面加上一个负号就是了

所以float的取值范围就是
-340282346638528859811704183484516925440 ~ 340282346638528859811704183484516925440
科学记数法就表示成
-3.402823e+38 ~ 3.402823e+38

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2022-3-4 19:35:53 | 显示全部楼层
这里用C语言验证一下,发现没有问题
$ cat main.c
#include <stdio.h>
#include <stdint.h>

void binary(char *buff, const void *data, size_t size) {
    uint8_t byte = *(const uint8_t *)(data + size - 1);
    for(size_t mask = 0x80; mask; mask >>= 1) {
        *buff++ = mask & byte ? '1': '0';
    }
    if(size == 1) {*buff = '\0'; return;}
    *buff++ = '_'; binary(buff, data, size - 1);
}

int main(void) {
    char buff[36];
    float data = 340282346638528859811704183484516925440.0;
    binary(buff, &data, sizeof(data));
    printf("float:\t%s\n", buff);
    return 0;
}
$ gcc-debug -o main main.c
$ ./main
float:        01111111_01111111_11111111_11111111
$
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-10-3 17:14

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表