Java算术编码 - 查找字符范围

时间:2015-12-04 14:09:29

标签: java compression precision rounding-error

我正在尝试重新创建算术编码的Java实现,如此链接中所述,在“算术编码:它如何工作”部分:link

我正处于需要沿概率线为各个符号分配范围的点。但是,我在创建正确的范围时遇到了一些问题。在下面显示的代码中,这是由setRanges()执行的。预期结果应为:

Character Ranges -

            0.0 - 0.09999999999999999
A           0.1 - 0.19999999999999999
B           0.2 - 0.29999999999999999
E           0.3 - 0.39999999999999999
G           0.4 - 0.49999999999999999
I           0.5 - 0.59999999999999999
L           0.6 - 0.79999999999999999
S           0.8 - 0.89999999999999999
T           0.9 - 0.99999999999999999

我目前的输出是:

角色范围 -

            0.0 - 0.09999999999999999
A           0.1 - 0.2
B           0.2 - 0.30000000000000004
E           0.30000000000000004 - 0.4
G           0.4 - 0.5
I           0.5 - 0.6
L           0.6 - 0.8
S           0.8 - 0.9
T           0.9 - 1.0

我不确定是否有更好的方法来编写我的方法setRanges(),或者这是否只是舍入错误的结果。

这是类Range,它只包含一个低和高浮点值:

public class Range {

    private double low, high;

    public Range(double low, double high) {
        this.low = low;
        this.high = high;
    }

    public String toString() {
        return low + " - " + high;
    }

}

方法:

import java.util.TreeMap;

    public static TreeMap<Character, Range> setRanges(TreeMap<Character, Double> treeMap) {
        TreeMap<Character, Range> rangeMap = new TreeMap<>();
        double currentValue;
        double previousValue = 0;
        double runningTotal = 0;

        for(Character key : treeMap.keySet()) {
            currentValue = treeMap.get(key) + runningTotal;
            rangeMap.put(key, new Range(previousValue, currentValue - 0.00000000000000001));
            previousValue = currentValue;
            runningTotal += treeMap.get(key);
        }
        return rangeMap;
    }

}

1 个答案:

答案 0 :(得分:2)

我认为你需要使用BigDecimal来达到这个精度。有128或没有roudning选项。见下文:

double first = 1d;
double second = 0.00000000000000001d;

System.out.println("Db --> " + (first - second));

BigDecimal firstBd = new BigDecimal(first);
BigDecimal secondBd = new BigDecimal(second);
BigDecimal resultBd = firstBd.subtract(secondBd);

System.out.println("32 --> " + resultBd.round(MathContext.DECIMAL32));
System.out.println("64 --> " + resultBd.round(MathContext.DECIMAL64));
System.out.println("128--> " + resultBd.round(MathContext.DECIMAL128));
System.out.println("Unl--> " + resultBd);

输出是:

  

Db - &gt; 1.0
32 - &gt; 1.000000
64 - &gt; 1.000000000000000
  128 - &GT; 0.9999999999999999899999999999999993
Unl - &gt;   0.9999999999999999899999999999999992845757594537807549147194381507675227382936355979836662299931049346923828125

相关问题