JDK-4644270 : EXCEPTION_FLT_DIVIDE_BY_ZERO with Floating Point Arithmetic using external dll
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 1.4.0
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: windows_nt
  • CPU: x86
  • Submitted: 2002-02-27
  • Updated: 2002-06-14
  • Resolved: 2002-06-14
Related Reports
Relates :  
Relates :  
Relates :  
Description

Name: jl125535			Date: 02/27/2002


FULL PRODUCT VERSION :
java version "1.4.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)


FULL OPERATING SYSTEM VERSION : Windows NT Version 4.0
(Build 1381, Service Pack 5)


A DESCRIPTION OF THE PROBLEM :
We use a dll to do some calculations. It is a C-dll calling
a Delphi-dll (pascal).
With one call to an standard arithmetic function in the
Delphi-dll (int(..)), returning to the Java VM and calling
an DecimalFormat.format, the Java VM will crash with
an "EXCEPTION_FLT_DIVIDE_BY_ZERO occurred at PC=0xADBD38".

The crash occures in DecimalFormat.format()
...
boolean isNegative = (number < 0.0) || (number == 0.0 &&
1/number < 0.0);
...
while dividing "1/number", if number is 0.0 (which is
normally allowed in Java -> Infinity).

The arithmetic function "int()" in the dll cuts the
fractional part of the number, so we assume a problem with
the floating point unit, but without any help we can't
verify that.
The "frac()"-function causes the same problem.
Without calling that functions, all works fine.

There is one other testcase, where we had a crash in the
awt.dll in older JDK-Versions, but this does not happen
with the actual version (1.4).
I think, that there is a relation between these crashes.
We have a complete small testcase for both problems.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Load external C-/Delphi-dll
2. Call external method, which uses described arithmetic
function ("int()" or "frac()")
3. Create an DecimalFormatter
4. Use DecimalFormat.format with an value of 0.0

ERROR MESSAGES/STACK TRACES THAT OCCUR :
C:\testdelphi>java test.TestDelphiDll
TestDelphiDll: TSTDELPH.DLL geladen
calling test_DelphiCall() ...
Java_test_TestDelphiDll_JniTestDelphi() calling   ...
Laden der Bibliothek lva32.dll!
Calling function entry() ...
entryRet = 456832
Java_test_TestDelphiDll_JniTestDelphi() finishing ...
test_DelphiCall() called ...
Now DecimalFormat.format ...

Unexpected Signal : EXCEPTION_FLT_DIVIDE_BY_ZERO occurred at PC=0xADBD38
Function=[Unknown.]
Library=(N/A)

NOTE: We are unable to locate the function name symbol for the error
      just occurred. Please refer to release documentation for possible
      reason and solutions.


Current Java thread:

Dynamic libraries:
0x00400000 - 0x00406000         c:\java\j2sdk1.4.0\bin\java.exe
0x77F70000 - 0x77FD0000         C:\WINNT\System32\ntdll.dll
0x77DC0000 - 0x77DFF000         C:\WINNT\system32\ADVAPI32.dll
0x77F00000 - 0x77F62000         C:\WINNT\system32\KERNEL32.dll
0x77E70000 - 0x77EC4000         C:\WINNT\system32\USER32.dll
0x77ED0000 - 0x77EFC000         C:\WINNT\system32\GDI32.dll
0x77E10000 - 0x77E67000         C:\WINNT\system32\RPCRT4.dll
0x78000000 - 0x78046000         C:\WINNT\system32\MSVCRT.dll
0x6D330000 - 0x6D442000         c:\java\j2sdk1.4.0\jre\bin\client\jvm.dll
0x77FD0000 - 0x77FFB000         C:\WINNT\System32\WINMM.dll
0x72FE0000 - 0x72FE8000         C:\WINNT\System32\mmdrv.dll
0x6BC00000 - 0x6BC17000         C:\WINNT\System32\sb16snd.dll
0x71700000 - 0x7178A000         C:\WINNT\system32\COMCTL32.dll
0x6D1D0000 - 0x6D1D7000         c:\java\j2sdk1.4.0\jre\bin\hpi.dll
0x6D300000 - 0x6D30D000         c:\java\j2sdk1.4.0\jre\bin\verify.dll
0x6D210000 - 0x6D228000         c:\java\j2sdk1.4.0\jre\bin\java.dll
0x6D320000 - 0x6D32D000         c:\java\j2sdk1.4.0\jre\bin\zip.dll
0x10000000 - 0x10033000         C:\testdelphi\tstdelph.dll
0x0AEC0000 - 0x0AED3000         C:\testdelphi\lva32.dll
0x65340000 - 0x653D2000         C:\WINNT\system32\oleaut32.dll
0x77B80000 - 0x77C36000         C:\WINNT\system32\ole32.dll
0x41000000 - 0x4100B000         C:\testdelphi\borlndmm.dll
0x76AE0000 - 0x76AFD000         C:\WINNT\System32\imagehlp.dll
0x731B0000 - 0x731BA000         C:\WINNT\System32\PSAPI.DLL

Local Time = Tue Feb 26 16:15:45 2002


****************
Another exception has been detected while we were handling last error.
Dumping information about last error:
ERROR REPORT FILE = (N/A)
PC                = 0x00ADBD38
SIGNAL            = -1073741682
FUNCTION NAME     = (N/A)
OFFSET            = 0xFFFFFFFF
LIBRARY NAME      = (N/A)
Please check ERROR REPORT FILE for further information, if there is any.
Good bye.


This bug can be reproduced always.

---------- BEGIN SOURCE ----------
We have a complete little testcase to reproduce the bug. Please ask for it.

The Java-Programm looks like:
import java.text.*;
import java.util.*;

public class TestDelphiDll
{
  private native int JniTestDelphi();

  public TestDelphiDll()
  {
    test_DelphiCall();
  }

  public static void main(String args[])
  {
    TestDelphiDll testDelphiDll1 = new TestDelphiDll();
    System.exit(0);
  }

  private void test_DelphiCall()
  {
    System.out.println("calling test_DelphiCall() ...");

    JniTestDelphi();

    System.out.println("test_DelphiCall() called ...");

    Double wertObject = new Double("0");
    double wertPre = wertObject.doubleValue();
    double wert = Math.abs(wertPre);

    DecimalFormat formatter;
    NumberFormat numberformat = NumberFormat.getInstance();
    if(numberformat instanceof DecimalFormat)
    {
      formatter = (DecimalFormat)numberformat;
    }
    else
    {
      NumberFormat numberformat1 = NumberFormat.getInstance(Locale.US);
      if(numberformat1 instanceof DecimalFormat)
        formatter = (DecimalFormat)numberformat1;
      else
        throw new Error("Could not get a DecimalFormat for the current
locale!!");
    }

    DecimalFormat decimalformat = (DecimalFormat)formatter.clone();
    StringBuffer stringbuffer = new StringBuffer();
    FieldPosition fieldposition = new FieldPosition(1);
    decimalformat.setMaximumFractionDigits(1000);
    decimalformat.setMinimumFractionDigits(0);

    // Now Division by zero
    System.out.println("Now DecimalFormat.format ...");
    decimalformat.format(wert, stringbuffer, fieldposition);
  }

  static
  {
      try
      {
          System.loadLibrary("TSTDELPH");
          System.out.println("TestDelphiDll: TSTDELPH.DLL geladen");
      }
      catch(UnsatisfiedLinkError e)
      {
          e.printStackTrace();
      }
  }
}

---------- END SOURCE ----------

CUSTOMER WORKAROUND :

======================================================================

You can patch the DecimalFormat-Class by detecting the sign
bit without Division by zero.

boolean isNegative = (number < 0.0) ||
        (number == 0.0 && (Double.doubleToLongBits(number)
& 0x8000000000000000L) != 0L);
        if (isNegative) number = -number;

Btw: Isn't this version not better for detecting the sign-
bit ?
(Review ID: 143369) 

Comments
EVALUATION Re-opening bug. Changing the new Double("0.0") to a new Double("1.0") or anything else but 0.0 works fine. It appears we may have a hole for 0.0 case. See Workaround from customer... ###@###.### 2002-06-13 Without running the attached test case, it sounds as if the C or Delphi dll is changing the floating-point unit's control word from traps disabled to (at least) trap on divide by zero. Java semantics require the former settings; other langauges require different settings, such as terminating on overflow or divide by zero. However, I would assume well-hehaved dll's *should* restore the floating-point settings of their callers. Executing 1.0/zero in Java is perfectly legal and well defined; moreover is it explicitly forbidden from throwing any sort of floating-point exception. If the FPU's control word has been modified, changing the DecimalFormat code to test the sign bit by converting to integer and masking only hides the problem since any subsequent divide by zero would trigger a program-terminating trap. Will investigate. ###@###.### 2002-06-13 Preliminary investigations reveal that Delphi does support changing the fpu's control word so the scenario outlined above is certainly possible. If the Delphi dll is alterning the control word and not changing it back, the bug to be fixed is in the Delphi dll. ###@###.### 2002-06-13 Confirmed previous suspicions: loading the Delphi dll changes the fpu's trapping status to trap on certain floating-point events (overflow, divide by zero, underflow) which are defined to not trap by Java. I modified the standalone test to perform some floating-point operations before and after the delphi call; the first set of operations caused a trap to occur, impliciating the loading process. (The call to DecimalFormat is not relevant or necessary to the trapping behavior.) This behavior is not so surprising since Delphi by default runs with floating-point traps enabled (see O'Reily's "Delphi in a Nutshell" p. 9). Well-written programs and dll's should restore the floating-point trapping status of their caller on exit, not propagate local changes to other parties. The customer has a few options: 1. Fix the Delphi code A Delphi program can explicitly save and restore the floating-point fpu state; the customer's program could properly shield the Java code from the unwanted fpu changes. See the Delphi documentation for the Set8087CW method. It is *not* resonable to place the onus of maintaining the proper fpu control state for cross-language calls on the jvm; the Delphi code is using non-default behavior. 2. Don't use Delphi for computations. From the description section, it sound as if the functionality the customer needs is basically provided by java.lang.Math.floor. intPortion(double x) { if(x >= 0) return(Math.floor(x); else return -Math.floor(-x); } The fractional portion of x is (x - intPortion(x)). The floor function is also found in the C math library. Closing as not a bug. ###@###.### 2002-06-14
14-06-2002

WORK AROUND You can patch the DecimalFormat-Class by detecting the sign bit without Division by zero. boolean isNegative = (number < 0.0) || (number == 0.0 && (Double.doubleToLongBits(number) & 0x8000000000000000L) != 0L); if (isNegative) number = -number; Btw: Isn't this version not better for detecting the sign- bit ? (Review ID: 143369) No, dividing zero into one is the only floating-point operation that can easily distinguish the sign of zero; this is perfectly legal and well-defined in Java. ###@###.### 2002-06-13
13-06-2002