2009-02-23 5 views
2

Я написал симулятор, который имеет некоторый код обнаружения столкновения и делает хороший бит математики на каждом объекте, когда он обнаруживает столкновения.Могу ли я заставить java выдавать ошибку при делении на ноль с числами с плавающей запятой?

Если эти два объекта находятся в одном и том же месте или в некоторых редких других случаях, я получаю NaN (не число) в качестве своего местоположения где-то вдоль линии, и я хотел бы знать, где. Как правило, программа будет разбиваться, если я делаю эти операции с целыми числами, но потому, что + и - бесконечность являются частью спецификации с плавающей точкой, это разрешено.

Итак, где-то вдоль линии я беру квадратный корень отрицательного числа или делясь на ноль.

В любом случае я могу сделать свою программу автоматически аварийной в операциях, которые вызывают это, поэтому я могу сузить его?

ответ

9

Я не думаю, что вы можете поднять деление на нулевое исключение, если вы не проверите числа перед делением и не создадите исключение самостоятельно.

Проблема с поплавками заключается в том, что стандарт требует, чтобы результат получал поплавок NaN (не число). И все JVM и компиляторы, которые я знаю, следуют стандарту в этом отношении.

0

Я не знаю ничего, что вы можете установить на виртуальной машине, чтобы это произошло.

В зависимости от того, как код структурирован Я хотел бы добавить следующее вроде проверки моих методов (я просто делаю это все время по привычке в основном - очень полезно, хотя):

float foo(final float a, final float b) 
{ 
    // this check is problematic - you really want to check that it is a nubmer very 
    // close to zero since floating point is never exact. 
    if(b == 0.0f) 
    { 
     throw new IllegalArgumentException("b cannot be 0.0f"); 
    } 

    return (a/b); 
} 

Если вам нужно точные представления чисел с плавающей запятой вам нужно посмотреть на java.math.BigDecimal.

0

Я могу посоветовать вам использовать АОП (например, AspectJ) для обнаружения исключений и предоставления дополнительной информации о времени выполнения.

два случая использования, которые могут иметь отношение:

  • Райта вокруг аспекта (ов), где вы ожидаете NaN и попытаться предотвратить NaN/Бесконечность и журнал работы информацию
  • райт аспект, который будет ловить исключение (ы) и предотвращение сбоя вашего программного обеспечения

В зависимости от того, как вы развертываете свое программное обеспечение, вы можете использовать различные стратегии плетения АОП (время выполнения, время загрузки и т. д.).

4

Вы можете обработать свои двоичные классы, ищущие fdiv операции, вставив чек для деления на ноль.

Java:

return x.getFloat()/f2; 

javap выход:

0: aload_0 
1: invokevirtual #22; //Method DivByZero$X.getFloat:()F 
4: fload_1 
5: fdiv 
6: freturn 

код замены, который бросает ArithemticException для деления на ноль:

0: aload_1 
1: invokevirtual #22; //Method DivByZero$X.getFloat:()F 
4: fstore_2 
5: fload_0 
6: fconst_0 
7: fcmpl 
8: ifne 21 
11: new  #32; //class java/lang/ArithmeticException 
14: dup 
15: ldc  #34; //String/by zero 
17: invokespecial #36; //Method java/lang/ArithmeticException."<init>":(Ljava/lang/String;)V 
20: athrow 
21: fload_2 
22: fload_0 
23: fdiv 
24: freturn 

Эта обработка может быть сделано использование байт-кода API, такие как ASM. Это не совсем тривиально, но это не ракетостроение.


Если все, что вы хотите, контроль (а не менять работу кода), то лучше подход может использовать отладчик. Я не уверен, какие отладчики позволят вам написать выражение, чтобы поймать то, что вы ищете, но нетрудно написать собственный отладчик. Sun JDK предоставляет код JPDA и пример кода, показывающий, как его использовать (unzip jdk/demo/jpda/examples.jar).

Пример кода, который подключается к разъему на локальном хосте:

public class CustomDebugger { 

    public static void main(String[] args) throws Exception { 
     String port = args[0]; 
     CustomDebugger debugger = new CustomDebugger(); 
     AttachingConnector connector = debugger.getConnector(); 
     VirtualMachine vm = debugger.connect(connector, port); 
     try { 
      // TODO: get & use EventRequestManager 
      vm.resume(); 
     } finally { 
      vm.dispose(); 
     } 
    } 

    private AttachingConnector getConnector() { 
     VirtualMachineManager vmManager = Bootstrap.virtualMachineManager(); 
     for (Connector connector : vmManager.attachingConnectors()) { 
      System.out.println(connector.name()); 
      if ("com.sun.jdi.SocketAttach".equals(connector.name())) { 
       return (AttachingConnector) connector; 
      } 
     } 
     throw new IllegalStateException(); 
    } 

    private VirtualMachine connect(AttachingConnector connector, String port) 
      throws IllegalConnectorArgumentsException, IOException { 
     Map<String, Connector.Argument> args = connector.defaultArguments(); 
     Connector.Argument pidArgument = args.get("port"); 
     if (pidArgument == null) { 
      throw new IllegalStateException(); 
     } 
     pidArgument.setValue(port); 

     return connector.attach(args); 
    } 
}