十进制补码算术实现中的 ArrayIndexOutOfBoundsException

Posted

技术标签:

【中文标题】十进制补码算术实现中的 ArrayIndexOutOfBoundsException【英文标题】:ArrayIndexOutOfBoundsException in ten's complement arithmetic implementation 【发布时间】:2015-01-26 16:57:56 【问题描述】:

我的代码尝试实现一个算法

从控制台获取用户输入的两个整数和一个操作数 +-, 将这些数字逐位存储在int[50] 中,以ten's complement 格式表示负数, 实现(十进制)逐位加/减运算, 以十进制格式打印结果,不带前导零。

但是,在我目前的实现中存在两个问题

添加 99 + 9999 时,打印结果为 01098,而不是预期的 010098。 当减去 99 - 9999 时,我得到一个 ArrayIndexOutOfBoundsException: 50 而不是预期的结果 -09900。
import java.util.*;

public class Program9 
    public static String getOperand() 
        Scanner scan = new Scanner(System.in);
        String stringOfInteger;

        System.out.print("Please enter an integer up to 50 numbers: ");
        stringOfInteger = scan.nextLine();
        return stringOfInteger;
    

    public static int[] convert(String operand) 
        int[] integer = new int[50];
        char ch;

        int position = operand.length() - 1;
        for (int i = integer.length - 1; i >= 0; i--) 
            if (position >= 0)
                ch = operand.charAt(position--);
            else
                ch = 0;
            if (ch >= '0' && ch <= '9') 
                integer[i] = ch - '0';
             else 
                integer[i] = 0;
            
        
        return integer;
    

    public static int[] add(int[] operand1, int[] operand2) 
        int[] result = new int[operand1.length];
        int carry = 0;
        for (int i = operand1.length - 1; i >= 0; i--) 
            result[i] = operand1[i] + operand2[i] + carry;
            if (result[i] / 10 == 1) 
                result[i] = result[i] % 10;
                carry = 1;
             else
                carry = 0;
        
        return result;
    

    public static int[] complement(int[] operand) 
        int[] result = new int[operand.length];
        for (int i = operand.length - 1; i >= 0; i--)
            result[i] = 9 - operand[i];
        return result;
    

    public static int[] add1(int[] operand) 
        int[] result = new int[50];
        result[49] = 1;
        for (int i = result.length - 2; i >= 0; i--)
            result[i] = 0;
        return result;
    

    public static int[] negate(int[] operand) 
        return add(add1(operand), complement(operand));
    

    public static void print(int[] result, String operation) 
        if (operation.charAt(0) == '+')
            System.out.print("The subtotal of the two integer = ");
        else if (operation.charAt(0) == '-')
            System.out.print("The substraction of the two integers = ");
        if (result[0] == 9) 
            result = negate(result);
            System.out.print("-");
            for (int i = 0; i < result.length; i++) 
                if (result[i] == 0 && result[i + 1] == 0)
                    continue;
                else
                    System.out.print(result[i]);
            
         else
            for (int i = 0; i < result.length; i++) 
                if (result[i] == 0 && result[i + 1] == 0)
                    continue;
                else
                    System.out.print(result[i]);
            
        System.out.println();
    

    public static void main(String[] args) 
        Scanner scan = new Scanner(System.in);
        int[] result = new int[50];
        String string1 = getOperand();
        String string2 = getOperand();
        int[] integer1 = convert(string1);
        int[] integer2 = convert(string2);
        String operation;

        System.out.print("Please enter which operation will be used (+ or -): ");
        operation = scan.nextLine();
        if (operation.charAt(0) == '+')
            add(integer1, integer2);
        else if (operation.charAt(0) == '-')
            integer2 = negate(integer2);

        result = add(integer1, integer2);

        System.out.println(Arrays.toString(integer1));
        System.out.println(Arrays.toString(integer2));
        System.out.println(Arrays.toString(add(integer1, integer2)));
        print(result, operation);
    

【问题讨论】:

【参考方案1】:

好的,在您的代码经过这么多讨论和这么多问题之后,我已经完全修改了您的原始代码,因为您说您想了解更多信息。在其他改进中,我做了以下更改:

Meaninfgul 类名 有意义的方法和参数名称 出于简洁的代码原因(文档、在一处轻松更改、有意义的名称)、运行时,将重复且经常使用的常量(如 50 和数字 1(求反所需)的数组表示形式)转换为静态最终成员优化)。 扩展代码以允许负整数作为操作数 为用户输入添加了验证模式。例如。现在检查最大数字长度以避免数组溢出。 通过使数组大于用户输入允许的最大位数来避免计算过程中的数字溢出(参见源代码 cmets) 为操作数和操作员输入添加带有错误处理的重试循环,将控制台处理提取到一个参数化方法中。 通过删除不必要的检查来简化代码,因为用户输入在转换为 int[] 之前已经过验证。 使调试输出可选
package de.scrum_master.***;

import java.util.Arrays;
import java.util.Scanner;
import java.util.regex.Pattern;

public class TensComplementArithmetic 
    // Print debug messages?
    private static final boolean DEBUG = true;

    // Maximum length for numbers entered by a user
    // (number of digits excluding the optional +/- sign)
    private static final int MAX_NUMBER_LENGTH = 50;

    // Array must have one additional element for the sign and
    // one more to avoid overflows when adding big negative numbers
    private static final int ARRAY_LENGTH = MAX_NUMBER_LENGTH + 2;

    // Scanner for console input handling
    private static final Scanner INPUT_SCANNER = new Scanner(System.in);

    // Regex pattern for positive/negative integer number format verification incl. length check
    private static final Pattern INTEGER_PATTERN = Pattern.compile("[+-]?[0-9]1," + MAX_NUMBER_LENGTH + "");

    // Regex pattern for operator verification (currently only "+"/"-" allowed)
    private static final Pattern OPERATOR_PATTERN = Pattern.compile("[+-]");

    // The number 1 is always needed for converting a 9's into a 10's complement
    // during negation, so we define it as a reusable constant
    private static final int[] NUMBER_ONE;

    static 
        // Initialise constant carrying array representation for number 1
        NUMBER_ONE = new int[ARRAY_LENGTH];
        NUMBER_ONE[ARRAY_LENGTH - 1] = 1;
    

    public static String readConsoleInput(String prompt, Pattern validationPattern, String errorMessage) 
        String input = null;
        while (input == null) 
            System.out.print(prompt + ": ");
            if (INPUT_SCANNER.hasNext(validationPattern))
                input = INPUT_SCANNER.next(validationPattern);
            else 
                INPUT_SCANNER.nextLine();
                System.out.println(errorMessage);
            
        
        return input;
    

    public static String getOperand(String operandName) 
        return readConsoleInput(
            "Operand " + operandName,
            INTEGER_PATTERN,
            "Illegal number format, please enter a positive/negative integer of max. " + MAX_NUMBER_LENGTH + " digits."
        );
    

    private static String getOperator() 
        return readConsoleInput(
            "Arithmetical operator (+ or -)",
            OPERATOR_PATTERN,
            "Unknown operator, try again."
        );
    

    public static int[] parseInteger(String number) 
        char sign = number.charAt(0);
        boolean isNegative = sign == '-' ? true : false;
        if (isNegative || sign == '+')
            number = number.substring(1);

        int[] result = new int[ARRAY_LENGTH];
        int parsePosition = number.length() - 1;
        for (int i = result.length - 1; i >= 0; i--) 
            if (parsePosition < 0)
                break;
            result[i] = number.charAt(parsePosition--) - '0';
        
        return isNegative ? negate(result) : result;
    

    public static int[] add(int[] operand1, int[] operand2) 
        int[] result = new int[ARRAY_LENGTH];
        int carry = 0;

        for (int i = ARRAY_LENGTH - 1; i >= 0; i--) 
            result[i] = operand1[i] + operand2[i] + carry;
            if (result[i] >= 10) 
                result[i] = result[i] % 10;
                carry = 1;
             else
                carry = 0;
        
        return result;
    

    public static int[] complement(int[] operand) 
        int[] result = new int[ARRAY_LENGTH];

        for (int i = operand.length - 1; i >= 0; i--)
            result[i] = 9 - operand[i];
        return result;
    

    public static int[] negate(int[] operand) 
        return add(complement(operand), NUMBER_ONE);
    

    public static void print(int[] result, String operation) 
        System.out.print(operation.charAt(0) == '-' ? "Difference = " : "Sum = ");
        if (result[0] == 9) 
            result = negate(result);
            System.out.print("-");
        
        boolean leadingZero = true;
        for (int i = 0; i < result.length; i++) 
            if (leadingZero) 
                if (result[i] == 0)
                    continue;
                leadingZero = false;
            
            System.out.print(result[i]);
        
        System.out.println(leadingZero ? "0" : "");
    

    public static void main(String[] args) 
        int[] operand1 = parseInteger(getOperand("#1"));
        int[] operand2 = parseInteger(getOperand("#2"));
        String operator = getOperator();

        if (operator.equals("-"))
            operand2 = negate(operand2);

        int[] result = new int[ARRAY_LENGTH];
        result = add(operand1, operand2);
        if (DEBUG) 
            System.out.println("Operand #1 = " + Arrays.toString(operand1));
            System.out.println("Operand #2 = " + Arrays.toString(operand2));
            System.out.println("Result     = " + Arrays.toString(result));
        
        print(result, operator);
    

【讨论】:

【参考方案2】:

免责声明:您的源代码有多个问题,但为了简单起见,我现在将忽略其中的大部分问题,并仅解释您的源代码出现问题的原因当前的问题并仅针对这些问题提出修复建议。

如果您检查 main 方法的数组输出,您会看到加法/减法结果看起来不错,即问题不在计算例程中,而是在打印例程中。你有

重复代码:for 循环打印正数/负数是相同的。 外观问题:始终打印一个前导零。 一个逻辑错误:您检查两个连续的零以确定前导零的结束位置和实际数字的开始位置。但是你忘记了 在一个数字内也可以有重复的零,例如在 10098 或 -9900 以内。这解释了为什么 10098 打印为 1098:您正在禁止打印第一个零。 如果最后一个数组元素(例如 9900)中存在零,则您无法检查(不存在的)后续元素而不会导致 ArrayIndexOutOfBoundsException。这解释了为什么会出现 -9900 异常。

现在你可以/应该做什么?

消除冗余的for 循环。您可以使用相同的循环来打印正数和负数。 使用boolean 标志来记住您是否仍在循环前导零。

您可以像这样更改打印方法:

public static void print(int[] result, String operation) 
    System.out.print(operation.charAt(0) == '-' ? "Difference = " : "Sum = ");
    if (result[0] == 9) 
        result = negate(result);
        System.out.print("-");
    
    boolean leadingZero = true;
    for (int i = 0; i < result.length; i++) 
        if (leadingZero) 
            if (result[i] == 0)
                continue;
            leadingZero = false;
        
        System.out.print(result[i]);
    
    System.out.println(leadingZero ? "0" : "");

【讨论】:

太棒了!它在大多数情况下都有效!但是,我还有两个小问题:*第一:减去342332 - 32,结果= 342299 ||应该在哪里 342300 * second:您在我的代码中发现的其他问题是什么。因为我还在学习过程中,我想向像你这样有经验的科学家学习,以提高我的编程技能。谢谢。 恕我直言:我不是心理学家。如果您不解释甚至不提及它们,我怎么知道您有更多问题?我不得不修改你的整个问题文本以使其更易于理解,因为另外两个人对此提出了抱怨。 很抱歉给您带来不便,我在写完评论之前误按了输入。 至于result == 0,修复很简单。我更新了答案。最后一条语句应该检查leadingZero 标志的状态并在这种情况下打印"0"。至于342332 - 32,我无法重现你的问题,结果是正确的。你改过代码了吗? 至于其他问题,我真的不想在这里一一列举,让我们保持话题。仅举一个例子:不可能输入负操作数。在我的answer 到您之前的问题中,我向您展示了一种解析整数的简单方法,您在这里不使用该方法。但是,请让我们在这里结束这个话题,好吗?这是一个问答网站,而不是论坛。对于您的下一个问题,请提出一个新问题。或者学习一些编程、阅读教程/书籍或只是练习。我推荐测试驱动编程 (TDD),即您可以检查自己的结果。【参考方案3】:

修复问题后的代码。感谢@kriegaex!

import java.util.*;
public class Program9 

  public static String getOperand() 
    Scanner scan = new Scanner(System.in);
    String stringOfInteger;
    
    System.out.print("Please enter an integer up to 50 numbers: ");
    stringOfInteger = scan.nextLine();
    return stringOfInteger;
  

  public static int[] convert(String operand) 
    int [] integer = new int[50];
    char ch;
    
    int position = operand.length() - 1;
    for (int i = integer.length - 1; i >= 0; i--) 
      if (position >= 0)
        ch = operand.charAt(position--);
      else
        ch = 0;
      if (ch >= '0' && ch <= '9') 
        integer[i] = ch - '0';
       else 
        integer[i] = 0;
      
    
    return integer;
  

  public static int[] add(int[] operand1, int[] operand2) 
    int [] result = new int[operand1.length];
    
    int carry = 0;
    for (int i = operand1.length - 1; i >= 0; i--) 
      result[i] = operand1[i] + operand2[i] + carry;
      if (result[i] / 10 == 1) 
        result[i] = result[i] % 10;
        carry = 1;
       else
        carry = 0;
    
    return result;
  

  public static int[] complement(int[] operand2)
    int [] result = new int[operand2.length];
    
    for (int i = operand2.length - 1; i >= 0; i--)
      result[i] = 9 - operand2[i];
    return result;
  

  public static int[] add1(int[] operand2)
    int [] result = new int[operand2.length];
    
    result[operand2.length - 1] = 1;
    for (int i = result.length - 2; i >= 0; i--)
      result[i] = 0;
    return result;
  

  public static int[] negate(int[] operand2)
    return add(add1(operand2), complement(operand2));
  

  public static void print(int[] result, String operation) 
    if (operation.charAt(0) == '+')
      System.out.print("The subtotal of the two integers = ");
    else if (operation.charAt(0) == '-')
      System.out.print("The subtraction of the two integers = ");
    
    if (result[0] == 9) 
        result = negate(result);
        System.out.print("-");
    
    boolean leadingZero = true;
    for (int i = 0; i < result.length; i++) 
      if (leadingZero) 
        if (result[i] == 0)
          continue;
        leadingZero = false;
      
      System.out.print(result[i]);
    
    if (leadingZero == true)
      System.out.println('0' - '0');
    System.out.println();
  

  public static void main(String[] args) 
    Scanner scan = new Scanner(System.in);
    int [] result = new int[50];
    String string1 = getOperand();
    String string2 = getOperand();
    int [] integer1 = convert(string1);
    int [] integer2 = convert(string2);
    String operation;
    
    System.out.print("Please enter which operation will be used (+ or -): ");
    operation = scan.nextLine();
    if (operation.charAt(0) == '+')
      add(integer1, integer2);
    else if (operation.charAt(0) == '-')
      integer2 = negate(integer2);
    
    result = add(integer1, integer2);
    
    System.out.println(Arrays.toString(integer1));
    System.out.println(Arrays.toString(integer2));
    System.out.println(Arrays.toString(add(integer1, integer2)));
    print(result, operation);
    
  

【讨论】:

这个答案完全没用,因为它只是重复原始代码加上我自己在print(int[], String) 中的错误修正。更糟糕的是,你拿走了我的错误修正 System.out.println(leadingZero ? "0" : ""); 并将其混淆为 if (leadingZero == true) System.out.println('0' - '0');。你为什么不直接写"0" 而写'0' - '0'?!?我强烈建议您再次删除您的答案,因为我的答案是正确的。 P.S.:你为什么将你的代码格式化为 javascript?是爪哇!无论如何,您都不能“运行 sn-p”。 您对原始代码所做的唯一更改是: (a) 在只有 one 操作数的方法中,将方法参数从 operand 重命名为 operand2,即只是混淆,完全没有意义。 (b) 你在文本提示中使用了错误的数学术语,而我已经修复了这些术语。加法的结果是 sum,而不是 subtotal。减法的结果是差异,而不是减法本身。除此之外和混淆的'0' - '0' 你没有改变任何东西。为什么不直接复制粘贴我的回答中的完整方法就可以了? 先生。 @kriegaex,感谢您的反馈和您的 cmets。我会尽力回答您的问题,对于让您和其他用户感到复杂的事情,我深表歉意。正如我之前提到的,我还在学习过程中,这实际上是我学习计算机科学的第一个学期。我缺乏英语是由于我的国籍。我不是母语人士,这让我的生活有点困难,尤其是在编程中使用数学术语时。 关于代码,我确实发现了我在笔记本电脑中使用的代码中错误地犯了一些错误。但是,您的代码工作正常。另外,我阅读了您关于leadingZeros 的cmets,并且在我的回答中确实使用了它们。我更改了您的错误修复System.out.println(leadingZero ? "0" : "");,因为作为编程初学者,这对我来说似乎很新。我还想更多地使用 if 语句,并使我的程序尽可能简单。 我知道这不一定更好,但我觉得这是从零开始学习的好方法,不使用其他人的代码和方法,并尝试更好地理解编程。我希望您能理解我做出这些更改的原因,我也希望您知道,我感谢您的关注,非常感谢。

以上是关于十进制补码算术实现中的 ArrayIndexOutOfBoundsException的主要内容,如果未能解决你的问题,请参考以下文章

执行以下 1 的补码定点整数算术运算

计算机中的算术运算

计算机如何实现运算?

ALU底层方法及计算机整数加减乘除模拟

二进制用补码做加减法怎么实现,举个例子??

是否有任何 C 的非二进制补码实现?