001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * Granite Data Services is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * Granite Data Services is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 020 * USA, or see <http://www.gnu.org/licenses/>. 021 */ 022package org.granite.messaging.jmf.codec.std.impl.util; 023 024import java.io.IOException; 025import java.io.OutputStream; 026 027import org.granite.messaging.jmf.InputContext; 028import org.granite.messaging.jmf.JMFEncodingException; 029import org.granite.messaging.jmf.OutputContext; 030 031/** 032 * @author Franck WOLFF 033 */ 034public class DoubleUtil { 035 036 // ------------------------------------------------------------------------ 037 038 public static class DoubleAsLong { 039 040 public final long longValue; 041 public final int pow10; 042 043 public DoubleAsLong(long longValue, int pow10) { 044 this.longValue = longValue; 045 this.pow10 = pow10; 046 } 047 } 048 049 public static DoubleAsLong doubleAsLong04(double v) { 050 long asLong = (long)v; 051 if (v == (double)asLong) 052 return new DoubleAsLong(asLong, 0); 053 054 asLong = (long)(v * 100.0); 055 if (v == (asLong / 100.0)) 056 return new DoubleAsLong(asLong, 2); 057 asLong += (asLong < 0 ? -1 : 1); 058 if (v == (asLong / 100.0)) 059 return new DoubleAsLong(asLong, 2); 060 061 asLong = (long)(v * 10000.0); 062 if (v == (asLong / 10000.0)) 063 return new DoubleAsLong(asLong, 4); 064 asLong += (asLong < 0 ? -1 : 1); 065 if (v == (asLong / 10000.0)) 066 return new DoubleAsLong(asLong, 4); 067 068 return null; 069 } 070 071 // ------------------------------------------------------------------------ 072 073 public static void encodeDouble(OutputContext ctx, double v) throws IOException { 074 LongUtil.encodeLong(ctx, Double.doubleToLongBits(v)); 075 } 076 077 public static double decodeDouble(InputContext ctx) throws IOException { 078 return Double.longBitsToDouble(LongUtil.decodeLong(ctx)); 079 } 080 081 // ------------------------------------------------------------------------ 082 083 /* 084 * Double special values: 085 * 086 * 0x7FF0000000000000L -> +Infinity 087 * 0x7FF0000000000001L ... 0x7FF7FFFFFFFFFFFFL -> 1st range of Quiet NaNs 088 * 0x7FF8000000000000L -> Canonical NaN 089 * 0x7FF8000000000001L ... 0x7FFFFFFFFFFFFFFFL -> 1st range of Signaling NaNs 090 * 0xFFF0000000000000L -> -Infinity 091 * 0xFFF0000000000001L ... 0xFFF7FFFFFFFFFFFFL -> 2nd range of Quiet NaNs 092 * 0xFFF8000000000000L ... 0xFFFFFFFFFFFFFFFFL -> 2nd range of Signaling NaNs 093 * 094 * Which gives the following 2-bytes prefixes: 095 * 096 * 0x7FF0 -> +Infinity 097 * 0x7FF8 -> NaN 098 * 0xFFF0 -> -Infinity 099 * 100 * 0x7FF1 ... 0x7FF7 \ 101 * 0x7FF9 ... 0x7FFF > Non canonical NaN prefixes. 102 * 0xFFF1 ... 0xFFFF / 103 */ 104 105 public static void encodeVariableDouble(OutputContext ctx, double v) throws IOException { 106 final OutputStream os = ctx.getOutputStream(); 107 108 long bits = Double.doubleToRawLongBits(v); 109 if ((bits & 0x7FF0000000000000L) == 0x7FF0000000000000L) { 110 os.write(0x7F); 111 if (bits == 0x7FF0000000000000L) // +Infinity 112 os.write(0xF0); 113 else if (bits == 0xFFF0000000000000L) // -Infinity 114 os.write(0xF1); 115 else // NaN (canonical or not) 116 os.write(0xF2); 117 } 118 else if (bits == 0x8000000000000000L) { // -0.0 119 os.write(0x7F); 120 os.write(0xF3); 121 } 122 else { 123 DoubleAsLong asLong = doubleAsLong04(v); 124 if (asLong != null && 125 asLong.longValue >= LongUtil.MIN_5_BYTES_VARIABLE_LONG && 126 asLong.longValue <= LongUtil.MAX_5_BYTES_VARIABLE_LONG) { 127 128 os.write(0xFF); 129 os.write(0xF0 | asLong.pow10); // 0xF0, 0xF2 or 0xF4. 130 LongUtil.encodeVariableLong(ctx, asLong.longValue); 131 } 132 else 133 LongUtil.encodeLong(ctx, bits); 134 } 135 } 136 137 public static double decodeVariableDouble(InputContext ctx) throws IOException { 138 139 int prefix = ctx.safeRead() << 8 | ctx.safeRead(); 140 switch (prefix) { 141 case 0x7FF0: 142 return Double.POSITIVE_INFINITY; 143 case 0x7FF1: 144 return Double.NEGATIVE_INFINITY; 145 case 0x7FF2: 146 return Double.NaN; 147 case 0x7FF3: 148 return -0.0; 149 } 150 151 if ((prefix & 0xFFF0) == 0xFFF0) { 152 long asLong = LongUtil.decodeVariableLong(ctx); 153 int pow10 = (prefix & 0x0F); 154 155 switch (pow10) { 156 case 0: 157 return asLong; 158 case 2: 159 return asLong / 100.0; 160 case 4: 161 return asLong / 10000.0; 162 default: 163 throw new JMFEncodingException("Unsupported power of 10: " + pow10); 164 } 165 } 166 167 return Double.longBitsToDouble(((long)prefix) << 48 | LongUtil.decodeLong(ctx, 5)); 168 } 169}