Issue Details (XML | Word | Printable)

Key: DERBY-2447
Type: Improvement Improvement
Status: Resolved Resolved
Resolution: Fixed
Priority: Minor Minor
Assignee: Knut Anders Hatlen
Reporter: Ole Solberg
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
Derby

ejbql and floattypes in org.apache.derbyTesting.functionTests.tests.lang.LangScripts intermittently fails with 'expected:<[0.0 ] > but was:<[-0.0] '

Created: 13/Mar/07 09:26 AM   Updated: 16/Jul/09 09:24 PM
Component/s: SQL
Affects Version/s: 10.3.1.4
Fix Version/s: 10.5.2.0, 10.6.0.0

Time Tracking:
Not Specified

File Attachments:
  Size
Text File Licensed for inclusion in ASF works d2447.diff 2009-05-06 01:03 PM Knut Anders Hatlen 6 kB
File Licensed for inclusion in ASF works negative-zero.diff 2009-04-23 06:20 PM Knut Anders Hatlen 2 kB
Java Source File Licensed for inclusion in ASF works Normalize.java 2009-04-23 01:03 PM Knut Anders Hatlen 0.5 kB
Environment:
OS: Red Hat Enterprise Linux AS release 4 (Nahant Update 3) 64bits - Linux 2.6.9-34.ELsmp #1 SMP Fri Feb 24 16:56:28 EST 2006 GNU/Linux
JVM: Sun Microsystems Inc. 1.5.0_07-b03

OS: Solaris 10 6/06 s10s_u2wos_09a SPARC 64bits - SunOS 5.10 Generic_118833-17
JVM: Sun Microsystems Inc. - 1.5.0_07-b03

Bug behavior facts: Regression Test Failure
Resolution Date: 06/May/09 10:41 PM
Labels:


 Description  « Hide
Seen intermittently on Linux since 2007-02-27.
Seen on Solaris 2007-03-12.

<signature>
ejbql(org.apache.derbyTesting.functionTests.tests.lang.LangScripts)junit.framework.ComparisonFailure: Output at line 454 expected:<[0.0 ] > but was:<[-0.0] >
at org.apache.derbyTesting.functionTests.util.CanonTestCase.compareCanon(CanonTestCase.java:100)
at org.apache.derbyTesting.functionTests.util.ScriptTestCase.runTest(ScriptTestCase.java:124)
at org.apache.derbyTesting.junit.BaseTestCase.runBare(BaseTestCase.java:80)
at junit.extensions.TestDecorator.basicRun(TestDecorator.java:24)
at junit.extensions.TestSetup$1.protect(TestSetup.java:21)
at junit.extensions.TestSetup.run(TestSetup.java:25)
</signature>

<signature>
floattypes(org.apache.derbyTesting.functionTests.tests.lang.LangScripts)junit.framework.ComparisonFailure: Output at line 1823 expected:<[0.0 ] > but was:<[-0.0] >
at org.apache.derbyTesting.functionTests.util.CanonTestCase.compareCanon(CanonTestCase.java:100)
at org.apache.derbyTesting.functionTests.util.ScriptTestCase.runTest(ScriptTestCase.java:124)
at org.apache.derbyTesting.junit.BaseTestCase.runBare(BaseTestCase.java:80)
at junit.extensions.TestDecorator.basicRun(TestDecorator.java:24)
at junit.extensions.TestSetup$1.protect(TestSetup.java:21)
at junit.extensions.TestSetup.run(TestSetup.java:25)
</signature>



 All   Comments   Work Log   Change History   Subversion Commits      Sort Order: Ascending order - Click to sort in descending order
Kathey Marsden added a comment - 11/Sep/07 03:30 PM
Unchecking Regession checkbox as this is not a confirmed product regression

Knut Anders Hatlen added a comment - 23/Apr/09 01:03 PM
I think this instability is caused by the JVM. At least if I run the attached Java class, which imitates the normalizeREAL() method in NumberDataType, the result of normalizeREAL(-0.0f) varies.

I've tested it on OpenSolaris with the following JVM:

java version "1.6.0_13"
Java(TM) SE Runtime Environment (build 1.6.0_13-b03)
Java HotSpot(TM) Server VM (build 11.3-b02, mixed mode)

It looks like this line in normalizeREAL() is optimized away when the run-time compiler translates the byte code to native code (haven't actually verified that, though):

    if (v == 0.0f) v = 0.0f;

This line is the one that's removing the sign for -0.0f, but I'll need to check if the language specification actually guarantees that it should work.

Bryan Pendleton added a comment - 23/Apr/09 04:29 PM
Hi Knut, this is an interesting observation, and an interesting little snippet of code.
What might I see when I run it? Am I doing it right:

-bash-2.05b$ javac Normalize.java
-bash-2.05b$ java -cp . Normalize
sum: 0.0
normalizeREAL(-0.0f): 0.0

I got these results with both of the following:

java version "1.5.0_05"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_05-b05)
Java HotSpot(TM) Client VM (build 1.5.0_05-b05, mixed mode, sharing)

java version "1.4.2_13"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_13-b06)
Java HotSpot(TM) Client VM (build 1.4.2_13-b06, mixed mode)

Knut Anders Hatlen added a comment - 23/Apr/09 05:38 PM
Yes, that's the correct way to run it.

The first line of output is not significant, it's just printed so that the run-time compiler is not allowed to optimize away the loop.

The second line prints either 0.0 or -0.0. I *think* the correct result is 0.0. However, when I run with the server VM I see -0.0 most of the time, but not always. I have only seen 0.0 printed with the client VM, probably because the client VM is not that eager to generate native code.

You could try "java -server Normalize" to force the use of the server VM.

Bryan Pendleton added a comment - 23/Apr/09 05:52 PM
What about writing that line as:

    if (v == -0.0f) v = 0.0f;

If that works, it might also have the desirable effect of making the intent of the code clearer.

Knut Anders Hatlen added a comment - 23/Apr/09 06:17 PM
Another alternative is to write a helper method which inspects the integer representation of the floating point value

    private static boolean isNegativeZero(double d) {
        // If the most significant bit (sign bit) is 1 and the rest are 0,
        // we have a negative zero.
        return Double.doubleToRawLongBits(d) == 0x8000000000000000L;
    }

and

    if (isNegativeZero(v)) {
        v = 0.0f;
    }

This has the additional advantage of only matching values that actually are negative zero.

Both approaches (comparing to -0.0f and using the helper method) appear to make the test program print 0.0 consistently.

I don't find anything in http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.2.3 indicating that there is something wrong with the current implementation of normalizeREAL() and normalizeDOUBLE(), so I'll file a bug against the JVM, at least to get a verification of whether or not it is a bug.

JVM bug or not, both of the suggested workarounds will make the code clearer, so I guess changing the Derby code wouldn't make any harm.

Knut Anders Hatlen added a comment - 23/Apr/09 06:20 PM
Here's a patch using the helper method approach. No tests have been run yet.

Knut Anders Hatlen added a comment - 24/Apr/09 09:10 AM
Derbyall and suites.All ran cleanly. The patch is ready for review.

Knut Anders Hatlen added a comment - 24/Apr/09 01:08 PM
I've logged a bug against Sun's JVM: http://bugs.sun.com/view_bug.do?bug_id=6833879
(The bug report is not visible yet.)

Bryan Pendleton added a comment - 24/Apr/09 02:26 PM
The fix seems fine to me. Given that we believe that the current code is actually
fine, and that this change is actually working around a bug in the JVM, it seems
like it would be nice to have a comment to that effect.

Do we feel that the existing regression test suite covers this area adequately?

Knut Anders Hatlen added a comment - 25/Apr/09 04:58 PM
Good point about adding a comment about the JVM bug, Bryan. And the more I think about it, the more I think your suggestion about just changing (v == 0.0f) to (v == -0.0f) is better (simpler and just as clear) than the extra helper method. So I'll post a new patch.

As to testing, it should be easy enough to write a test that fails reliably on a JVM with this bug. For instance, we could prepare the statement "VALUES -CAST(? AS REAL)", set the parameter to 0 and execute it 10000 times. After about 5000-6000 executions it should stop returning 0.0 and start returning -0.0. I can add a test case doing that in the next patch (unless someone thinks it's a bad idea to add a regression test for something that's arguably a JVM bug).

Changing issue type from bug to improvement, since this is not a Derby bug and we just add a workaround that happens to make the intention of the code clearer.

Bryan Pendleton added a comment - 25/Apr/09 05:37 PM
I think the test would be useful. Even though it's not our bug, it's behavior that we depend on, so having a test which verifies the expected behavior seems helpful.

Thanks for taking care of this in a thorough fashion. Much appreciated!

Also, I see that the JVM bug report is now visible and has been marked "accepted" by Sun.

Knut Anders Hatlen added a comment - 06/May/09 07:29 AM
It looks like a fix for this has been committed in the OpenJDK repository now: http://hg.openjdk.java.net/jdk7/hotspot-comp/hotspot/rev/36ee9b69616e

Knut Anders Hatlen added a comment - 06/May/09 01:03 PM
Here's a new patch which includes test cases that expose the bug. Three methods are affected, but only two of them are tested. The untested method is only called from CastNode when a constant double value is cast to a float, in which case the constant double has already been normalized so a negative zero will never be seen there. All three methods were updated for consistency.

I haven't run the full regression test suite yet.

Bryan Pendleton added a comment - 06/May/09 01:48 PM
Thanks for adding the test, Knut. It looks good to me. I can't think of anything else needed here. +1.

Knut Anders Hatlen added a comment - 06/May/09 10:41 PM
Thanks, Bryan. I committed the patch to trunk with revision 772449. I also merged it to 10.5 and committed revision 772452, so that we'll get less noise in the release testing when we create a 10.5 maintenance release.