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

发布于 2024-07-13 14:58:46 字数 270 浏览 9 评论 0原文

我编写了一个模拟器,它有一些碰撞检测代码,并在检测到碰撞时对每个对象进行大量数学计算。

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

因此,在这条线上的某个地方,我取负数的平方根或除以零。

无论如何,我是否可以让我的程序在导致此问题的操作上自动崩溃,以便我可以缩小范围?

I wrote a simulator that has some collision detection code and does a good bit of math on each object when it detects collisions.

If these two objects are at the exact same location or in some rare other cases, I'm getting NaN (not a number) as their location somewhere along the line and I'd like to know where. Normally, the program would crash if I did these operations on integers but because + and - infinity are part of the floating point spec, it is allowed.

So, somewhere along the line I'm taking a square root of a negative number or dividing by zero.

Is there anyway I can make my program automatically crash on the operations that cause this so I can narrow it down some?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

自此以后,行同陌路 2024-07-20 14:58:46

我认为您不能引发除以零的异常,除非您在除法之前测试数字并自己引发异常。

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

I don't think you can raise a divide by zero exception unless you test the numbers before the division and raise the exception yourself.

The problem with floats is, that the standard requires that the result gets a NaN (Not a Number) float. And all JVMs and compilers that I am aware off follow the standard in this respect.

冷︶言冷语的世界 2024-07-20 14:58:46

您可以处理二进制类,查找 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 和示例显示如何使用它的代码(解压缩 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);
    }
}

You could process your binary classes looking for fdiv operations, inserting a check for divide by zero.

Java:

return x.getFloat() / f2;

javap output:

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

Replacement code that throws a ArithemticException for divide-by-zero:

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

This processing can be done using bytecode manipulation APIs like ASM. This isn't really trivial, but it isn't rocket science either.


If all you want is monitoring (rather than changing the operation of the code), then a better approach might be to use a debugger. I'm not sure what debuggers would allow you to write an expression to catch what you're looking for, but it isn't difficult to write your own debugger. The Sun JDK provides the JPDA and sample code showing how to use it (unzip jdk/demo/jpda/examples.jar).

Sample code that attaches to a socket on 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);
    }
}
北笙凉宸 2024-07-20 14:58:46

扩展 McDowell 处理二进制类的建议,我编写了一些代码,这些代码已成功用于相对较大的代码库,我想分享: https://bitbucket.org/Oddwarg/java-sigfpe-emulator/

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

原则上,对字节码汇编的修改非常简单:当遇到 fdivddiv 指令时,调用相应的 notZero首先插入函数。

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 类型帧之间的距离保持不变低于阈值。 它们根据需要重复。

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

Expanding on McDowell's proposal to process the binary classes, I wrote some code that I've used with success for a relatively large codebase, and I'd like to share: https://bitbucket.org/Oddwarg/java-sigfpe-emulator/

I used Krakatau to disassemble and reassemble class files. A small Java class containing the public, static helper methods float notZero(float f) and double notZero(double f) must be added to the application as a part of the process.

The modification to the bytecode assembly is, in principle, very simple: When an fdiv or ddiv instruction is encountered, a call to the appropriate notZero function is inserted first.

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

In this example, the line between L29 and L30 was inserted by the program.

The notZero call consumes the top of the operand stack as its argument, which is the divisor. If the divisor is not zero, then it is returned, putting it back on top of the operand stack. If the divisor is zero, then an ArithmeticException is thrown instead.

Calling a method and making sure the operand stack remains the same avoids most of the problems related to Stack Map Frames and operand stack overflow, but I did have to ensure the distance between .stack same-type frames remains below a threshold. They are repeated on demand.

I hope this will be useful to someone. I've spent more than enough time manually searching for floating point divisions by zero myself.

倾`听者〃 2024-07-20 14:58:46

我不知道你可以在虚拟机中设置什么来实现这一点。

根据你的代码的结构,我会在我的方法中添加以下类型的检查(我只是出于习惯而一直这样做 - 虽然非常有用):

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。

I am not aware of anything you can set in the VM to make that happen.

Depending on how your code is structured I would add the following sort of checks to my methods (I just do this all the time out of habit mostly - very useful though):

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

If you need exact representations of floating point numbers you need to look at java.math.BigDecimal.

や三分注定 2024-07-20 14:58:46

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

两个可能相关的用例:

  • 编写您期望 NaN 并尝试防止 NaN/Infinity 的方面,并记录运行时信息
  • 编写将捕获异常并防止软件崩溃的方面

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

I can advise you to use AOP (e.g. AspectJ) for catching exceptions and providing you additional run-time information.

Two use cases that can be relevant:

  • wright around aspect(s) where you expect NaN and try to prevent NaN/Infinity and log run-time information
  • wright an aspect that will catch exception(s) and prevent your software from crashing

Depends on how you deploy your software, you can use different AOP weaving strategies (run-time, load-time etc.).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文