gpt4 book ai didi

java - 在处理 "long"的大值时,这段代码有什么问题?

转载 作者:行者123 更新时间:2023-11-29 10:06:15 24 4
gpt4 key购买 nike

我编写了一个实用程序类,用于在自定义 numeral system 中使用基数 N 对数字进行编码。作为任何有自尊心的 Java 程序员,我随后编写了一个单元测试来检查代码是否按预期工作(对于我可以抛出的任何数字) .

事实证明,对于少数人来说,它是有效的。然而,对于足够大的数字,测试失败了。

代码:

public class EncodeUtil {

private String symbols;

private boolean isCaseSensitive;
private boolean useDefaultSymbols;

private int[] symbolLookup = new int[255];

public EncodeUtil() {
this(true);
}

public EncodeUtil(boolean isCaseSensitive) {
this.useDefaultSymbols = true;
setCaseSensitive(isCaseSensitive);
}

public EncodeUtil(boolean isCaseSensitive, String symbols) {
this.useDefaultSymbols = false;
setCaseSensitive(isCaseSensitive);
setSymbols(symbols);
}

public void setSymbols(String symbols) {
this.symbols = symbols;
fillLookupArray();
}

public void setCaseSensitive(boolean isCaseSensitive) {
this.isCaseSensitive = isCaseSensitive;
if (useDefaultSymbols) {
setSymbols(makeAlphaNumericString(isCaseSensitive));
}
}

private void fillLookupArray() {
//reset lookup array
for (int i = 0; i < symbolLookup.length; i++) {
symbolLookup[i] = -1;
}
for (int i = 0; i < symbols.length(); i++) {
char c = symbols.charAt(i);
if (symbolLookup[(int) c] == -1) {
symbolLookup[(int) c] = i;
} else {
throw new IllegalArgumentException("duplicate symbol:" + c);
}
}
}

private static String makeAlphaNumericString(boolean caseSensitive) {
StringBuilder sb = new StringBuilder(255);
int caseDiff = 'a' - 'A';
for (int i = 'A'; i <= 'Z'; i++) {
sb.append((char) i);
if (caseSensitive) sb.append((char) (i + caseDiff));
}
for (int i = '0'; i <= '9'; i++) {
sb.append((char) i);
}
return sb.toString();
}

public String encodeNumber(long decNum) {
return encodeNumber(decNum, 0);
}

public String encodeNumber(long decNum, int minLen) {
StringBuilder result = new StringBuilder(20);
long num = decNum;
long mod = 0;
int base = symbols.length();
do {
mod = num % base;
result.append(symbols.charAt((int) mod));
num = Math.round(Math.floor((num-mod) / base));
} while (num > 0);
if (result.length() < minLen) {
for (int i = result.length(); i < minLen; i++) {
result.append(symbols.charAt(0));
}
}
return result.toString();
}

public long decodeNumber(String encNum) {
if (encNum == null) return 0;
if (!isCaseSensitive) encNum = encNum.toUpperCase();
long result = 0;
int base = symbols.length();
long multiplier = 1;
for (int i = 0; i < encNum.length(); i++) {
char c = encNum.charAt(i);
int pos = symbolLookup[(int) c];
if (pos == -1) {
String debugValue = encNum.substring(0, i) + "[" + c + "]";
if (encNum.length()-1 > i) {
debugValue += encNum.substring(i + 1);
}
throw new IllegalArgumentException(
"invalid symbol '" + c + "' at position "
+ (i+1) + ": " + debugValue);
} else {
result += pos * multiplier;
multiplier = multiplier * base;
}
}
return result;
}

@Override
public String toString() {
return symbols;
}

}

测试:

public class EncodeUtilTest {

@Test
public void testRoundTrip() throws Exception {
//for some reason, numbers larger than this range will not be decoded correctly
//maybe some bug in JVM with arithmetic with long values?
//tried also BigDecimal, didn't make any difference
//anyway, it is highly improbable that we ever need such large numbers
long value = 288230376151711743L;
test(value, new EncodeUtil());
test(value, new EncodeUtil(false));
test(value, new EncodeUtil(true, "1234567890qwertyuiopasdfghjklzxcvbnm"));
}

@Test
public void testRoundTripMax() throws Exception {
//this will fail, see above
test(Long.MAX_VALUE, new EncodeUtil());
}

@Test
public void testRoundTripGettingCloserToMax() throws Exception {
//here we test different values, getting closer to Long.MAX_VALUE
//this will fail, see above
EncodeUtil util = new EncodeUtil();
for (long i = 1000; i > 0; i--) {
System.out.println(i);
test(Long.MAX_VALUE / i, util);
}
}

private void test(long number, EncodeUtil util) throws Exception {
String encoded = util.encodeNumber(number);
long result = util.decodeNumber(encoded);
long diff = number - result;
//System.out.println(number + " = " + encoded + " diff " + diff);
assertEquals("original=" + number + ", result=" + result + ", encoded=" + encoded, 0, diff);
}

}

任何想法为什么当值变大时事情开始失败?我也试过 BigInteger,但似乎没有什么不同。

最佳答案

您在 encodeNumber 方法中使用了 float 学运算,这使得您的代码依赖于 double 类型的精度。

替换

num = Math.round(Math.floor((num-mod) / base));

num = (num - mod) / base;

使测试通过。其实

num = num / base;

应该也能正常工作(思考实验:当 / 是整数除法时,19/10 是什么?)。

关于java - 在处理 "long"的大值时,这段代码有什么问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7388421/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com