将 BigDecimal/BigInteger 序列化为 ProtocolBuffers 的最佳方法是什么

发布于 2024-07-25 18:45:26 字数 385 浏览 4 评论 0原文

我开始将自定义序列化机制迁移到协议缓冲区。 BigDecimal 是一种特别经常使用的数据类型。

有谁知道在协议缓冲区中序列化这个的好方法? 我们当前的序列化例程使用 BigDecimal.toPlainString() 进行序列化,并使用 new BigDecimal(String) 进行反序列化 - 我假设有更好的方法。

我的猜测是将 BigDecimal 定义为:

message BDecimal {
    required int32 scale = 1;
    required BInteger int_val = 2;
}

但我不太确定如何定义 BigInteger - 也许使用其 toByteArray() 方法?

I am starting to migrate a custom serialization mechanism to Protocol Buffers. One data type that will be used particularly regularly is BigDecimal.

Does anyone know of a good way of serializing this within Protocol Buffers? Our current serialization routine uses BigDecimal.toPlainString() for serialization, and new BigDecimal(String) for deserialization - I'm assuming there's a better way.

My guess is to define a BigDecimal as:

message BDecimal {
    required int32 scale = 1;
    required BInteger int_val = 2;
}

But I am not too sure how to define BigInteger - perhaps using its toByteArray() method?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

顾忌 2024-08-01 18:45:26

是的。 您应该将 BigInteger 定义为 BigInteger.toByteArray() 。

我的猜测是 BigDecimal 将是:


message BDecimal {
  required int32 scale = 1;
  required BInteger int_val = 2;
}

而 BigInteger 可能定义为


message BInteger {
  required bytes value = 1;
}

处理 BigInteger 的代码将是:


  BInteger write(BigInteger val) {
    BInteger.Builder builder = BInteger.newBuilder();
    ByteString bytes = ByteString.copyFrom(val.toByteArray());
    builder.setValue(bytes);
    return builder.build();
  }

  BigInteger read(BInteger message) {
    ByteString bytes = message.getValue();
    return new BigInteger(bytes.toByteArray());
  }

Yes. You should define BigInteger as BigInteger.toByteArray() .

My guess is that BigDecimal would be:


message BDecimal {
  required int32 scale = 1;
  required BInteger int_val = 2;
}

while BigInteger may be defined as


message BInteger {
  required bytes value = 1;
}

The code to handle BigInteger would be:


  BInteger write(BigInteger val) {
    BInteger.Builder builder = BInteger.newBuilder();
    ByteString bytes = ByteString.copyFrom(val.toByteArray());
    builder.setValue(bytes);
    return builder.build();
  }

  BigInteger read(BInteger message) {
    ByteString bytes = message.getValue();
    return new BigInteger(bytes.toByteArray());
  }
柠檬色的秋千 2024-08-01 18:45:26

我最近有与OP相同的需求,并使用了与@notnoop提出的类似的解决方案。 看到 @stikkos 的评论后将其发布在这里:

如何将 BigDecimal 转换为 BigInteger 并进行缩放? 然后回来?

根据BigDecimal文档

BigDecimal 的值为 (unscaledVal × 10-scale),根据精度舍入和舍入模式设置。

因此,可以考虑三个属性来序列化 java.math.BigDecimal:


现在,代码。

  1. Protobuf v3 消息定义:

    syntax = "proto3"; 
    
      消息十进制值 { 
        uint32 比例 = 1; 
        uint32 精度 = 2; 
        字节值 = 3; 
      } 
      
  2. 如何将 java.math.BigDecimal 序列化为 Protobuf 消息:

    导入 com.google.protobuf.ByteString; 
      导入十进制值; 
    
      java.math.BigDecimal bigDecimal = new java.math.BigDecimal("1234.56789"); 
      DecimalValue 序列化 = DecimalValue.newBuilder() 
              .setScale(bigDecimal.scale()) 
              .setPrecision(bigDecimal. precision()) 
              .setValue(ByteString.copyFrom(bigDecimal.unscaledValue().toByteArray())) 
              。建造(); 
      
  3. 如何将 Protobuf 消息反序列化回 java.math.BigDecimal

    java.math.MathContext mc = new java.math.MathContext(serialized.getPrecision()); 
      java.math.BigDecimal 反序列化 = new java.math.BigDecimal( 
              新的 java.math.BigInteger(serialized.getValue().toByteArray()), 
              序列化.getScale(), 
              MC); 
      
  4. 我们可以检查获得的 java.math.BigDecimal 是否产生相同的原始值,如下所示:

    断言 bigDecimal.equals(反序列化); 
      

注意:OP 假设有一种比 java.math.BigDecimal 更好的方法来序列化 java.math.BigDecimal使用普通的字符串。 在存储方面,这个解决方案更好。

例如,要序列化 ​​π 的前 100 位小数:

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679
System.out.println("DecimalValue: " + serialized.getSerializedSize());
System.out.println("String: " + StringValue.of(pi).getSerializedSize());

这会产生:

DecimalValue: 48
String: 104

I have recently had the same need as OP, and used a similar solution to the one proposed by @notnoop. Posting it here after seeing this comment from @stikkos:

How would you convert a BigDecimal to a BigInteger and scale? And back ?

According to the BigDecimal class documentation:

The value of the BigDecimal is (unscaledVal × 10-scale), rounded according to the precision and rounding mode settings.

So, a java.math.BigDecimal can be serialized considering three properties:


Now, the code.

  1. Protobuf v3 message definition:

    syntax = "proto3";
    
    message DecimalValue {
      uint32 scale = 1;
      uint32 precision = 2;
      bytes value = 3;
    }
    
  2. How to serialize a java.math.BigDecimal to a Protobuf message:

    import com.google.protobuf.ByteString;
    import DecimalValue;
    
    java.math.BigDecimal bigDecimal = new java.math.BigDecimal("1234.56789");
    DecimalValue serialized = DecimalValue.newBuilder()
            .setScale(bigDecimal.scale())
            .setPrecision(bigDecimal.precision())
            .setValue(ByteString.copyFrom(bigDecimal.unscaledValue().toByteArray()))
            .build();
    
  3. How to deserialize the Protobuf message back to a java.math.BigDecimal:

    java.math.MathContext mc = new java.math.MathContext(serialized.getPrecision());
    java.math.BigDecimal deserialized = new java.math.BigDecimal(
            new java.math.BigInteger(serialized.getValue().toByteArray()),
            serialized.getScale(),
            mc);
    
  4. We can check the obtained java.math.BigDecimal yields the same original value as follows:

    assert bigDecimal.equals(deserialized);
    

Note: OP assumed there is a better way to serialize a java.math.BigDecimal than using a plain String. In terms of storage, this solution is better.

For example, to serialize the first 100 decimals of π:

3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679
System.out.println("DecimalValue: " + serialized.getSerializedSize());
System.out.println("String: " + StringValue.of(pi).getSerializedSize());

This yields:

DecimalValue: 48
String: 104
习惯成性 2024-08-01 18:45:26

为什么要改变它? 只是因为您可以或者确实有需要(例如分析会话确认,序列化/反序列化花费了大部分时间)。

我会使用一个字符串,只是因为它是内置的:)

建议的字节数组方法(将 BigDecimal/BigInteger 序列化到 ProtocolBuffers 的最佳方法是什么)对我来说似乎很好,如果字符串表示似乎是一个问题。

Why do you want to change it? Just because you can or is there a real need (like a profiling session confirming, that serialization/deserialization takes most of the time).

I would use a string, just because it is built in :)

The proposed byte array approach (What is the best approach for serializing BigDecimal/BigInteger to ProtocolBuffers) seems fine to me, if string representation seems to be an issue.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文