🙊 前言:我是阿瑞,一个热爱技术、喜欢教学、对编程充满热情、痴迷于代码世界、喜欢研读源码、尝试自研开发框架的探索者。今天,与大家分享一个特别的纪念日——我的 CSDN 技术博客创作第 128 天的纪念日
机缘
我接触 CSDN 是因为 2019 年刚步入大学时,我的 C++ 课程教授黄老师疯狂安利博客,于是我在 2019-12-08 加入了 CSDN 大家庭。直至一个平凡的日子,2024年01月04日,但对我来说,它却意义非凡。正是在这一天,我决定将自己所学到的知识和经验,通过文字的形式,分享给更多的人。因此我撰写了人生中的第 1 篇技术博客——《瑞_Java中浮点数精度误差产生原因_浮点数底层存储结构(详细附代码)》,从此这平凡的一天,我赋予了它不平凡的意义。
在注册账号后通过四年多的学习和工作,我自身在学习的过程中摸着黑,不断尝试,不断探寻路线,试图开辟一条属于自己适合自己的路。这个过程异常艰辛,也让我积累了不少的经验,于是我寻思着,要是能将自己日常的学习笔记、工作经验以及自己研究的过程,通过博客的形式与大家交流分享,不仅可以记录这宝贵的学习成长过程,也可以作为学习参考资料帮助到大家,圆自己一个教学梦。
每当我看到自己的博客被大家阅读、点赞、收藏、评论,想到自己能够帮助到别人,我就感到无比的满足和快乐
收获
这128天里,累积发布原创博客64篇,访问量12W+、点赞量2000+、收藏量1300+、粉丝2000+
我个人非常喜欢教学和分享,并且非常热衷于对其他人分享这份热情,非常沉迷于将他人教会的成就感。每当我将博客分享给同事朋友的时候,听到那一声豁然开朗的OH OH OH ~~~
的时候,真的有说不出的成就感💗
这128天里,我经历了许多,也收获了许多。我经历了从一个初出茅庐的新手博主到一个逐渐成熟的创作者的转变。从一开始的 Markdown 都不会使用,到各种语法的排版、设计专栏和自己的 logo 、形成自己的创作模版、不断磨练着自己的写作技巧,每一篇文章都希望以更好的排版、更清晰的步骤逻辑展现给大家。大家的每一次阅读、点赞、收藏、评论都是非常宝贵的精神鼓励,每当我迷茫消沉想躺平的时候,总是能看到 CSDN 发送的文章三连通知,激励着我继续坚持下去,尤其是我的一位不可或缺的挚友 黄宇轩 ,每当我想要放弃坚持的时候,能让我在疲惫和困惑时给予我慰藉和勇气。所以,真的非常!非常!非常!感谢大家的支持💗💗💗
日常
在这 128 天里,我也许时常会因为工作和出差的繁忙而疲于更新博客,但每当我重新打开 CSDN 时,看到大家的支持,那份分享的热情总是能够迅速点燃。因为我知道,技术博客不仅是我个人成长的记录,更是与广大技术同好者交流的平台。在这里,大家互相分享自己的笔记、见解和经验,也可以从他人的博客中汲取知识,大家互相进步。
如今,坚持创作、持续输出,通过博客向大家分享交流我的知识和经验已经是我生活中不可缺少的一部分。虽然上班出差回到家后已经精疲力尽,但由于对于分享的执念非常之深,而时间嘛,挤挤总是有的。我最亲爱的老舅曾告诉我:在繁忙的日常工作中,不仅要踏实地低头干活,也要偶尔抬头仰望星空,不断的了解和学习前沿技术,不断的充实自己的技术水平,同时也要注意锻炼身体,健康才是革命的本钱!
这 128 天里,我经历了许多,收获了许多。只要平时有空的时候,我就将自己的学习笔记、思考、实践和经验化作一篇篇博客,这些图文不仅记录了我的学习成长历程,也见证了我对技术的执着追求。
其实写了博客之后才知道,写博客其实是非常花费时间和精力的。需要自己先非常了解该技术,再构思介绍该知识点的逻辑思路,又要结合图文进行排版美化,突出重点,减少读者的阅读成本,我自己曾经也是读者,知道排版不够直观的博客基本上不会多看一眼,但自己写的时候才知道,这是一件十分困难的事情,而且出于处女座的原因,对完美总是有莫名的执念,见不得自己的博客有任何瑕疵,即使发布之后也会多次阅读检查,只要发现问题就会立马修改。
虽然写博客需要花费大量的时间精力,但与之而来的收获也是相当之大。正所谓教学相长也,如果通过我的博客教会了一个原本不懂该知识点的人,这说明了我已经对该知识点处于“精通”的水平,才能做到让他人成功理解。如果只是看教学视频跟着绝对能得到答案的过程敲一遍代码,过一段时间就会慢慢忘记该知识点,编程是需要自己上手多敲多练多尝试多思考才能真正提升的,在写博客的过程中就是自己上手实践的一个过程,因为我要保证我的读者能通过我的博客成功解决问题,所以我会尝试各种可能遇到的情况、思考大家可能提出的问题,以此丰富博客的内容,这个过程就能获得对一个知识点的深刻理解。
成就
关于代码成就,我想了很久,最终决定展示我的第一篇博客的代码,它虽不是我最有技术水平的代码,但是我最有代表性的一段思维逻辑代码,体现了我思考问题的习惯和思路。
以下代码主要是辅助验证 float 的实际存储值,以此向读者说明了 Java 中浮点数精度误差产生原因以及展示了浮点数底层存储的具体结构,如果感兴趣,可见《瑞_Java中浮点数精度误差产生原因_浮点数底层存储结构》
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
/**
* 浮点数底层研究
*
* @author LiaoYuXing-Ray
* @version 1.0
* @createDate 2024/1/4 10:01
**/
public class FloatAnalysis{
public static void main(String[] args) throws Exception {
// 需要计算真实存储值的浮点数
float floatValue1 = 16.35f;
float floatValue2 = .35f;
System.out.println(floatValue1 + "的真实存储值:" + rayFloatStudy(floatValue1));
System.out.println("\n========我是一条华丽的分割线========\n");
System.out.println(floatValue2 + "的真实存储值:" + rayFloatStudy(floatValue2));
}
/**
* 解析浮点数组成
*
* @param floatValue 待解析的浮点数
* @author LiaoYuXing-Ray 2024/1/4 10:01
**/
public static double rayFloatStudy(float floatValue) throws Exception {
// 符号位(1位)
byte[] symbol = new byte[1];
// 阶码位(8位),指数部分
byte[] exponent = new byte[8];
// 尾数位(23位),有效数字
byte[] number = new byte[23];
String binaryRepresentation = floatToBinary(floatValue);
System.out.println("浮点数\t" + floatValue);
System.out.println("========开始解析每一位的含义========");
System.out.println(binaryRepresentation);
byte[] byteString = binaryRepresentation.getBytes();
// float 4字节 1字节=8bit 所以32
byte[] byteArray = new byte[32];
// ASCII码中48是0 49是1
for (int i = 0; i < byteString.length; i++) {
if (byteString[i] == 48) {
byteArray[i] = 0;
} else if (byteString[i] == 49) {
byteArray[i] = 1;
} else {
throw new Exception("不应该出现的分支,理论上转为二进制只有0和1");
}
}
/*
以下是获取符号位、指数部分、有效数字的值,并输出
*/
for (int i = 0; i < byteArray.length; i++) {
if (i == 0) {
symbol[i] = byteArray[i];
}
if (i >= 1 && i <= 8) {
exponent[i - 1] = byteArray[i];
}
if (i > 8) {
number[i - 9] = byteArray[i];
}
// 以下为输出,可注释
if (i != 0 && i % 4 == 0) {
System.out.print("-");
}
System.out.print(byteArray[i]);
}
System.out.print("\n符号位值(1位):" + Arrays.toString(symbol));
if (symbol[0] == 0) {
System.out.print("为正数");
} else if (symbol[0] == 1) {
System.out.print("为负数");
} else {
throw new Exception("不应该出现的分支,符号位理论上只有0和1两种情况");
}
System.out.print("\n指数部分(8位):[");
for (byte b : exponent) {
System.out.print(b);
}
System.out.print("]\t-> 转化十进制数:[" + binaryToDecimal(exponent) + "]");
System.out.print("\n有效数字(23位):[");
for (byte b : number) {
System.out.print(b);
}
System.out.print("]");
System.out.println("\n=======还原float的真实存储值=======");
// 指数部分的十进制数值
int exponentOfDecimalNumber = binaryToDecimal(exponent);
/*
以 IEEE754 标准规定,单精度的阶码偏移量为 2^(n-1)-1 (即127),这样能表示的指数范围为 [-126,127]
*/
// 阶码偏移量
int offsetExponent = exponentOfDecimalNumber - 127;
System.out.println("阶码偏移量=指数部分的十进制数值[" + exponentOfDecimalNumber + "]- [2^(n-1)-1 (即127)]=" + offsetExponent);
// 指数值。此处double是因为Math.pow方法的参数为double类型
double integerBitsBaseValue;
if (offsetExponent >= 0) {
integerBitsBaseValue = 1 << offsetExponent;
} else {
integerBitsBaseValue = Math.pow(2D, offsetExponent);
}
System.out.println("指数值=2^阶码偏移量[" + offsetExponent + "]=" + integerBitsBaseValue);
// 整数部分(小数点左边的部分)即整数位(基础值)
double integerBits = integerBitsBaseValue;
// 有效位数转化为10进制数
double tempCount;
if (offsetExponent >= 0) {
tempCount = 1D;
// 如果偏移量大于等于0,整数部分为0
integerBits = 0;
System.out.println("整数部分(小数点左边的部分)即整数位(基础值)=" + integerBits);
System.out.println("尾数23位实际为1.xxx,尾数为有效数字加上1.0");
System.out.print("所以有效位数的二进制表示为[1.");
} else {
tempCount = 0D;
System.out.println("整数部分(小数点左边的部分)即整数位(基础值):" + integerBits);
System.out.println("尾数23位部分为0.xxx,尾数为有效数字加上0.0");
System.out.print("所以有效位数的二进制表示为[0.");
}
/*
此处计算小数二进制转化为十进制,比如0.01(2)=0*2^(-1)+1*2^(-2)=0.25(10)
*/
for (int i = 0; i < number.length; i++) {
if (number[i] == 1) {
// 将2的负(i+1)次方累加
tempCount += Math.pow(2, -(i + 1));
}
System.out.print(number[i]);
}
System.out.println("] -> 有效位数10进制数的值" + tempCount);
// 小数有效值
double decimalEffectiveValue = tempCount * integerBitsBaseValue;
System.out.println("(有效位数10进制数的值" + tempCount + ")*(指数值" + integerBitsBaseValue + ")=小数有效值:" + decimalEffectiveValue);
double result = decimalEffectiveValue + integerBits;
if (byteArray[0] == 0) {
System.err.println(floatValue + "为正数,小数有效值[" + decimalEffectiveValue + "]加上整数部分[" + integerBits + "],最终结果:" + result);
} else {
System.err.println(floatValue + "为负数结果需要*(-1),小数有效值[" + decimalEffectiveValue + "]加上整数部分[" + integerBits + "],最终结果:" + (result * -1));
}
// 休眠是因为防止err语句输出顺序混乱
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}
/**
* 长度为8的byte[]转换为对应的十进制数
*
* @param exponentBytes 长度为8的byte[]
* @return int
* @author LiaoYuXing-Ray 2024/1/4 10:01
**/
private static int binaryToDecimal(byte[] exponentBytes) {
int decimal = 0;
for (int i = 0; i < exponentBytes.length; i++) {
decimal += (exponentBytes[i] & 0xFF) * Math.pow(2, exponentBytes.length - 1 - i);
}
return decimal;
}
/**
* 浮点型转化为bit,字符串输出,会自动补零
*
* @param value 浮点数
* @return java.lang.String
* @author LiaoYuXing-Ray 2024/1/4 10:01
**/
private static String floatToBinary(float value) {
int intBits = Float.floatToIntBits(value);
return String.format("%32s", Integer.toBinaryString(intBits)).replace(' ', '0');
}
}
感谢 CSDN 平台,让我们相遇相识,并着我一路上的点点滴滴,每当看到消息右上角的小红点时总是充满期待,点开之后更是能获得难以用语言形容的成就感、幸福感、满足感。我也要感谢自己,感谢自己当初的选择,感谢自己一路的坚持。在此,我也想对自己说一声加油,告诉自己要坚持创作,不断提升自己,争取取得更多的成就和认可
憧憬
我的创作初心是为了分享知识和经验,在这个过程中帮助大家的同时提升自己的技术水平,今后我也会继续努力创作,并将这份对技术的热情持续分享给大家
我最大的愿望就是能开发出一款属于自己原创设计的Java应用程序框架rayframework
,目前在设计中,我会坚持开源,让知识产生更多的交互、分享,从而提高整个行业的水平,为 Java 技术社区做出更大的贡献