dubbo序列化问题(一)浮点数问题
dubbo是一个分布式服务框架,在国内比较常用,在开发过程中遇到一个浮点数反序列化问题。
问题描述,当参数是float类型的3.7,反序列化却得到了一个double类型的值:3.700000047683716。
然后,我写了个测试程序:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import com.alibaba.com.caucho.hessian.io.Hessian2Input;
import com.alibaba.com.caucho.hessian.io.Hessian2Output;
public class TestHessionLite {
public static void main(String[] args) throws IOException {
HashMap<String,Float> map=new HashMap<String,Float>();
Float loat=new Float(3.7);
map.put("3.7", loat);
byte[] aa=TestHessionLite.serialize(map);
Object mm=TestHessionLite.deserialize(aa);
System.out.println(mm.toString());
}
public static byte[] serialize(Object obj) throws IOException{
ByteArrayOutputStream os = new ByteArrayOutputStream();
Hessian2Output ho = new Hessian2Output(os);
byte[] cc = null;
try {
if(obj==null) throw new NullPointerException();
ho.writeObject(obj);
ho.flushBuffer();
cc=os.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}finally{
ho.close();
}
return cc;
}
public static Object deserialize(byte[] by) throws IOException{
try {
if(by==null) throw new NullPointerException();
ByteArrayInputStream is = new ByteArrayInputStream(by);
Hessian2Input hi = new Hessian2Input(is);
return hi.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
输出结果为
{3.7=3.700000047683716}
然后由分别使用3.5、3.6进行测试
{3.6=3.5999999046325684}
{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不是通过二进制存储,是通过字节数组来进行存储,这样可以保证数据不用进行转化。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import org.apache.commons.codec.binary.Base64;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.serializers.JavaSerializer;
public class TestKryo {
public static void main(String[] args) {
HashMap<String,Float> map=new HashMap<String,Float>();
Float loat=new Float(3.7);
map.put("value", loat);
String aa=TestKryo.serialize(map);
Object mm=TestKryo.deserialize(aa,HashMap.class);
System.out.println(mm.toString());
}
private static <T extends Serializable> String serialize(T obj) {
Kryo kryo = new Kryo();
kryo.setReferences(false);
kryo.register(obj.getClass(), new JavaSerializer());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Output output = new Output(baos);
kryo.writeClassAndObject(output, obj);
output.flush();
output.close();
byte[] b = baos.toByteArray();
try {
baos.flush();
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
return new String(new Base64().encode(b));
}
@SuppressWarnings("unchecked")
private static <T extends Serializable> T deserialize(String obj,
Class<T> clazz) {
Kryo kryo = new Kryo();
kryo.setReferences(false);
kryo.register(clazz, new JavaSerializer());
ByteArrayInputStream bais = new ByteArrayInputStream(
new Base64().decode(obj));
Input input = new Input(bais);
return (T) kryo.readClassAndObject(input);
}
}
还没有评论,来说两句吧...