dubbo序列化问题(一)浮点数问题

水深无声 2022-04-06 10:44 175阅读 0赞

dubbo是一个分布式服务框架,在国内比较常用,在开发过程中遇到一个浮点数反序列化问题。

问题描述,当参数是float类型的3.7,反序列化却得到了一个double类型的值:3.700000047683716。

然后,我写了个测试程序:

  1. import java.io.ByteArrayInputStream;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;
  4. import java.util.HashMap;
  5. import com.alibaba.com.caucho.hessian.io.Hessian2Input;
  6. import com.alibaba.com.caucho.hessian.io.Hessian2Output;
  7. public class TestHessionLite {
  8. public static void main(String[] args) throws IOException {
  9. HashMap<String,Float> map=new HashMap<String,Float>();
  10. Float loat=new Float(3.7);
  11. map.put("3.7", loat);
  12. byte[] aa=TestHessionLite.serialize(map);
  13. Object mm=TestHessionLite.deserialize(aa);
  14. System.out.println(mm.toString());
  15. }
  16. public static byte[] serialize(Object obj) throws IOException{
  17. ByteArrayOutputStream os = new ByteArrayOutputStream();
  18. Hessian2Output ho = new Hessian2Output(os);
  19. byte[] cc = null;
  20. try {
  21. if(obj==null) throw new NullPointerException();
  22. ho.writeObject(obj);
  23. ho.flushBuffer();
  24. cc=os.toByteArray();
  25. } catch (Exception e) {
  26. e.printStackTrace();
  27. }finally{
  28. ho.close();
  29. }
  30. return cc;
  31. }
  32. public static Object deserialize(byte[] by) throws IOException{
  33. try {
  34. if(by==null) throw new NullPointerException();
  35. ByteArrayInputStream is = new ByteArrayInputStream(by);
  36. Hessian2Input hi = new Hessian2Input(is);
  37. return hi.readObject();
  38. } catch (Exception e) {
  39. e.printStackTrace();
  40. }
  41. return null;
  42. }
  43. }

输出结果为

  1. {3.7=3.700000047683716}

然后由分别使用3.5、3.6进行测试

  1. {3.6=3.5999999046325684}
  2. {3.5=3.5}

经过测试,并不是所有小数都有问题,部分小数会出现序列化问题。

我的dubbo服务序列化使用的是dubbo默认的hession2,而使用hessian2协议,也就是传输对象序列化,它是二进制的RPC协议。

经过分析,问题应该是出在了十进制浮点数转二进制。

后面又查看了相关资料,以及写了十进制和二进制互转的测试程序发现,就是不分小数在float单精度下是无法表示出来的。具体原因可以见下面资料http://blog.csdn.net/zcczcw/article/details/7362473。

如果将float,改成double,就不存在刚才精度问题了,因为double是双精度,可以保存64位二进制;

但是当小数点超过8位时,double也会被截取。

而是用kryo进行序列化则不会出现上面问题,因为kryo不是通过二进制存储,是通过字节数组来进行存储,这样可以保证数据不用进行转化。

  1. import java.io.ByteArrayInputStream;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;
  4. import java.io.Serializable;
  5. import java.util.HashMap;
  6. import org.apache.commons.codec.binary.Base64;
  7. import com.esotericsoftware.kryo.Kryo;
  8. import com.esotericsoftware.kryo.io.Input;
  9. import com.esotericsoftware.kryo.io.Output;
  10. import com.esotericsoftware.kryo.serializers.JavaSerializer;
  11. public class TestKryo {
  12. public static void main(String[] args) {
  13. HashMap<String,Float> map=new HashMap<String,Float>();
  14. Float loat=new Float(3.7);
  15. map.put("value", loat);
  16. String aa=TestKryo.serialize(map);
  17. Object mm=TestKryo.deserialize(aa,HashMap.class);
  18. System.out.println(mm.toString());
  19. }
  20. private static <T extends Serializable> String serialize(T obj) {
  21. Kryo kryo = new Kryo();
  22. kryo.setReferences(false);
  23. kryo.register(obj.getClass(), new JavaSerializer());
  24. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  25. Output output = new Output(baos);
  26. kryo.writeClassAndObject(output, obj);
  27. output.flush();
  28. output.close();
  29. byte[] b = baos.toByteArray();
  30. try {
  31. baos.flush();
  32. baos.close();
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. }
  36. return new String(new Base64().encode(b));
  37. }
  38. @SuppressWarnings("unchecked")
  39. private static <T extends Serializable> T deserialize(String obj,
  40. Class<T> clazz) {
  41. Kryo kryo = new Kryo();
  42. kryo.setReferences(false);
  43. kryo.register(clazz, new JavaSerializer());
  44. ByteArrayInputStream bais = new ByteArrayInputStream(
  45. new Base64().decode(obj));
  46. Input input = new Input(bais);
  47. return (T) kryo.readClassAndObject(input);
  48. }
  49. }

发表评论

表情:
评论列表 (有 0 条评论,175人围观)

还没有评论,来说两句吧...

相关阅读

    相关 FPGA点数定点

    因为在普通的fpga芯片里面,寄存器只可以表示无符号型,不可以表示小数,所以在计算比较精确的数值时,就需要做一些处理,不过在altera在Arria 10 中增加了硬核浮点D