[Java SE] 经典问题:超出Java Long型(8字节/64位)的二进制比特流数据如何进行大数的数值计算?

0 问题描述

  • 经典问题:超出Java Long型(8字节/64位)的二进制比特流数据如何进行大数的数值计算?

近期工作上遇到了这个问题:需要将一个无符号数、且位长>=8字节(等于8字节时,首位bit为1,其他bit不全为0)的二进制字符串 转为Java****对象(原始整数 ),进行整型 运算、或浮点数运算

浮点运算的思路:result = 原始整数 * 精度 + 偏移量

  • 解决思路:将二进制字符串转为byte数组,再转为BigInteger大整型数。如果基于进行浮点运算时,可将 BigInteger 大整型数对象再转为 BigDecimal。

new BigDecimal v = new BigDecimal(new BigInteger(xxx))

1 解决过程示例

  • 二进制数据:"1100000001000000110010110000000000000000000000000000000000000000" (需考虑------------情况1:作为有符号数情况2:作为无符号数

16进制:0xc040cb0000000000L

shell 复制代码
11000000 
01000000
11001011
00000000
00000000
00000000
00000000
00000000

1.1 测试用例1:无符号数、且位长>=8字节(等于8字节时,首位bit为1,其他bit不全为0)的情况

java 复制代码
    /** 针对 长度为 64 bit、无符号数 的CAN信号,且第1位为1的情况 :使用 BigInteger
     * @description Java中没有内置的无符号8字节整数类型,但是可以使用 `java.math.BigInteger` 类来处理任意大的整数值,包括无符号整数
     * @refernce-doc
     **/
    public static void unsigned8BytesDataTest(){
        // 一个8字节的无符号整数
        long longValue =  0xc040cb0000000000L; //0x10000000000000000L;
        String longStr = "c040cb0000000000";//canFrameContent
        // 转为二进制字符串
        String binStr = BytesUtil.hexStringToBinaryString(longStr);
        System.out.println("binStr: " + binStr);//1100000001000000110010110000000000000000000000000000000000000000

        // 将无符号长整数转换为 BigInteger | 方式1: BigInteger
        BigInteger value = toUnsignedBigInteger(longValue);
        System.out.println("value : " + value);//1385 3295 6546 5208 4224

        //二进制字符串转Java数据对象 | 测验 Long.parseLong(binStr , 2) | 若没有报错,则说明OK
        BigInteger value2 = toUnsignedBigInteger(binStr);
        System.out.println("value2 : " + value2);//1385 3295 6546 5208 4224

        //二进制字符串转Java数据对象 | 测验 Long.parseLong(binStr , 2) | 若没有报错,则说明OK
        Long value3 = Long.parseLong(binStr, 2);
        System.out.println("value3 : " + value3);//报错信息如下
//        Exception in thread "main" java.lang.NumberFormatException: For input string: "1100000001000000110010110000000000000000000000000000000000000000"
//        at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
//        at java.lang.Long.parseLong(Long.java:592)
//        at ParseTest.unsigned8BytesDataTest(ParseTest.java:213)
//        at ParseTest.main(ParseTest.java:29)
        }

1.2 测试用例2:有符号数、且位长>=8字节(等于8字节时,首位bit为1,其他bit不全为0)的情况

java 复制代码
    /**
     * 有符号数、8字节
     * 最终目标: 二进制字符串 转 Java 数据对象
     */
    public static void signed8BytesDataTest(){
        // 一个8字节的无符号整数
        long longValue =  0xc040cb0000000000L; //0x10000000000000000L;
        String longStr = "c040cb0000000000";//canFrameContent
        // 转为二进制字符串
        String binStr = BytesUtil.hexStringToBinaryString(longStr);
        System.out.println("binStr: " + binStr);//1100000001000000110010110000000000000000000000000000000000000000

        // 将有符号长整数转换为 BigInteger | 方式1: BigInteger
        BigInteger value = toUnsignedBigInteger(longValue);
        System.out.println("value : " + value);//-459 3448 4190 5746 7392

        //二进制字符串转Java数据对象 | 测验 Long.parseLong(binStr , 2) | 若没有报错,则说明OK
        BigInteger value2 = toUnsignedBigInteger(binStr);
        System.out.println("value2 : " + value2);//1385 3295 6546 5208 4224

        //二进制字符串转Java数据对象 | 测验 Long.parseLong(binStr , 2) | 若没有报错,则说明OK
        Long value3 = Long.parseLong(binStr, 2);
        System.out.println("value3 : " + value3);//报错信息如下
//        Exception in thread "main" java.lang.NumberFormatException: For input string: "1100000001000000110010110000000000000000000000000000000000000000"
//        at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
//        at java.lang.Long.parseLong(Long.java:592)
//        at ParseTest.signed8BytesDataTest(ParseTest.java:241)
//        at ParseTest.main(ParseTest.java:30)
    }

1.X 工具方法

toUnsignedBigInteger(long unsignedLong/String binStr)

java 复制代码
    private static BigInteger toUnsignedBigInteger(long unsignedLong) {
        // 将无符号的8字节长整数转换为字节数组
        byte[] bytes = ByteBuffer.allocate(8).putLong(unsignedLong).array();

        // 使用字节数组创建BigInteger
        return new BigInteger(1, bytes);
    }

    /** 二进制字符串 **/
    private static BigInteger toUnsignedBigInteger(String binStr) {
        byte[] bytes = null;
        try {
            // 将无符号的8字节长整数转换为字节数组
            bytes = BytesUtil.binaryStringToBinaryArray(binStr);
        } catch (Exception exception) {
            log.error("Fail to convert as big integer!binStr : {}, exception : {}", binStr, exception);
        }

        // 使用字节数组创建BigInteger
        return new BigInteger(1, bytes);
    }

binaryStringToBinaryArray(binStr)

java 复制代码
    /**
     * 二进制字符串转二进制数组
     * @param binaryString
     * @return
     */
    public static byte[] binaryStringToBinaryArray(String binaryString) {
        if(ObjectUtils.isEmpty(binaryString)){
            throw new RuntimeException("Fail to convert binary array cause by the empty binary string! binaryString : " + binaryString);
        }
        if(binaryString.length() %8 != 0){//不是8的倍数
            throw new RuntimeException("Fail to convert binary array cause that the binary string is not a multiple of 8! binaryString : " + binaryString);
        }

//        char [] charArray =  binaryString.toCharArray() // string 内部由 2个字节的char组成的 char 数组 , 故: 这种转换做法有风险
//        byte [] binaryArray = new byte [ binaryString.length() ];
//        for (int i = 0; i < charArray.length; i ++) {
//            //binaryArray[i] = (byte)charArray[i]; // java char 占用 : 2个字节 ; java byte 占用 1个字节 => 这种做法不正确
//            binaryArray[i]
//        }

        int byteSize = binaryString.length()/8;
        byte[] binaryArray = new byte[byteSize];
        for (int i = 0; i < byteSize; i ++) {
            String byteBinaryStr = binaryString.substring(i*8, i*8 + 8);//sample "01001000"
            binaryArray[i] = binaryStringToByte(byteBinaryStr);
        }
        return binaryArray;
    }

X 参考文献

相关推荐
编程火箭车1 天前
【Java SE 基础学习打卡】34 数组的定义与初始化
java se·java 基础·java 数组·数组定义与初始化·静态初始化·动态初始化·length 属性
编程火箭车2 天前
【Java SE 基础学习打卡】33 数组的概述
java se·java 基础·数组概述·数组核心特征·java 数组·批量存储数据·连续内存存储
编程火箭车3 天前
【Java SE 基础学习打卡】32 方法的嵌套调用与递归调用
java se·java 基础·java 方法·方法嵌套调用·方法递归调用·递归终止条件·递归应用场景
编程火箭车4 天前
【Java SE 基础学习打卡】31 方法的返回值与void关键字
java se·java 基础·return 语句·编程小白入门·java 方法·方法返回值·void 关键字
编程火箭车6 天前
【Java SE 基础学习打卡】28 方法的定义与调用
java se·参数传递·返回值·java 基础·新手避坑·java 方法·方法定义与调用
编程火箭车19 天前
【Java SE 基础学习打卡】27 方法的概述
编程语法·java se·代码复用·编程小白入门·方法概述·功能模块化·java 方法
编程火箭车1 个月前
【Java SE 基础学习打卡】24 循环结构 - while
java·编程基础·循环结构·while循环·java se·do-while循环·避免死循环
编程火箭车1 个月前
【Java SE 基础学习打卡】23 分支语句 - switch
编程语法·java se·switch 语句·新手避坑·java 分支结构·编程小白入门·等值判断
BillKu4 个月前
Java核心概念详解:JVM、JRE、JDK、Java SE、Java EE (Jakarta EE)
java·jvm·jdk·java ee·jre·java se·jakarta ee
千千寰宇1 年前
[车联网/计算机网络] Autosar 的 `ARXML` 配置数据库文件协议
xml·计算机网络·智能网联汽车-can总线协议·汽车/新能源汽车/智能网联汽车