我可以强制java在用浮点数除以零时抛出错误吗?

时间:2009-02-23 01:19:06

标签: java floating-point divide-by-zero

我写了一个有一些碰撞检测代码的模拟器,并在检测到碰撞时对每个对象进行了很好的数学运算。

如果这两个对象位于完全相同的位置或者在一些罕见的其他情况下,我将NaN(不是数字)作为它们在线的某个位置,我想知道在哪里。通常,如果我对整数执行这些操作,程序会崩溃,但因为+和 - 无穷大是浮点规范的一部分,所以允许它。

所以,沿着这条线的某个地方,我采用负数的平方根或除以零。

无论如何,我可以让我的程序自动崩溃导致这种情况的操作,所以我可以将其缩小一些吗?

5 个答案:

答案 0 :(得分:9)

除非您在分割前测试数字并自行提出异常,否则我认为您不能提出除零除外。

浮点数的问题是,标准要求结果得到NaN(非数字)浮点数。我所知道的所有JVM和编译器都遵循这方面的标准。

答案 1 :(得分:4)

您可以处理二进制类,查找 fdiv 操作,插入除以零的检查。

爪哇:

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

此处理可以使用ASM等字节码操作API完成。这不是真正的微不足道,但它也不是火箭科学。


如果您只想监视(而不是更改代码的操作),那么更好的方法可能是使用调试器。我不确定哪些调试器会允许你编写表达式以捕获你正在寻找的东西,但是编写自己的调试器并不困难。 Sun JDK提供了JPDA和示例代码,展示了如何使用它(解压缩jdk / demo / jpda / examples.jar)。

附加到localhost上的套接字的示例代码:

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);
    }
}

答案 2 :(得分:1)

在McDowell提出的处理二进制类的建议的基础上,我编写了一些已经成功用于相对较大的代码库的代码,我想分享一下:https://bitbucket.org/Oddwarg/java-sigfpe-emulator/

我使用Krakatau来反汇编和重新组装类文件。包含公共静态辅助方法float notZero(float f)double notZero(double f)的小型Java类必须作为过程的一部分添加到应用程序中。

从原则上讲,对字节码程序集的修改非常简单:遇到fdivddiv指令时,首先插入对相应notZero函数的调用。 / p>

L28:    fload_0 
L29:    fload_1 
        invokestatic Method owg/sigfpe/SIGFPE notZero (F)F 
L30:    fdiv 
L31:    fstore_2 

在此示例中,程序插入了L29和L30之间的行。

notZero调用使用操作数堆栈的顶部作为其参数,即除数。如果除数不为零,则将其返回,并将其放回操作数堆栈的顶部。如果除数为零,则抛出ArithmeticException

调用方法并确保操作数堆栈保持相同可避免大多数与堆栈映射框架和操作数堆栈溢出有关的问题,但我确实必须确保.stack same型框架之间的距离保持在阈。根据需要重复它们。

我希望这对某人有用。我花了足够多的时间手动搜索自己被零归零的浮点数。

答案 3 :(得分:0)

我不知道您可以在VM中设置任何可以实现此目的的任何内容。

根据你的代码的结构,我会在我的方法中添加以下类型的检查(我只是习惯性地做这个习惯 - 但非常有用):

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。

答案 4 :(得分:0)

我可以建议您使用AOP(例如AspectJ)来捕获异常并为您提供额外的运行时信息。

两个可能相关的用例:

  • 围绕您期望NaN的方面,并尝试阻止NaN / Infinity并记录运行时信息
  • 怀疑将捕获异常并阻止您的软件崩溃的方面

取决于您如何部署软件,您可以使用不同的AOP编织策略(运行时,加载时间等)。