Skip to content
This repository has been archived by the owner on Feb 2, 2023. It is now read-only.

8253409: Double-rounding possibility in float fma #154

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
41 changes: 15 additions & 26 deletions src/java.base/share/classes/java/lang/Math.java
Expand Up @@ -1768,32 +1768,21 @@ public static double fma(double a, double b, double c) {
*/
@HotSpotIntrinsicCandidate
public static float fma(float a, float b, float c) {
/*
* Since the double format has more than twice the precision
* of the float format, the multiply of a * b is exact in
* double. The add of c to the product then incurs one
* rounding error. Since the double format moreover has more
* than (2p + 2) precision bits compared to the p bits of the
* float format, the two roundings of (a * b + c), first to
* the double format and then secondarily to the float format,
* are equivalent to rounding the intermediate result directly
* to the float format.
*
* In terms of strictfp vs default-fp concerns related to
* overflow and underflow, since
*
* (Float.MAX_VALUE * Float.MAX_VALUE) << Double.MAX_VALUE
* (Float.MIN_VALUE * Float.MIN_VALUE) >> Double.MIN_VALUE
*
* neither the multiply nor add will overflow or underflow in
* double. Therefore, it is not necessary for this method to
* be declared strictfp to have reproducible
* behavior. However, it is necessary to explicitly store down
* to a float variable to avoid returning a value in the float
* extended value set.
*/
float result = (float)(((double) a * (double) b ) + (double) c);
return result;
if (Float.isFinite(a) && Float.isFinite(b) && Float.isFinite(c)) {
if (a == 0.0 || b == 0.0) {
return a * b + c; // Handled signed zero cases
} else {
return (new BigDecimal((double)a * (double)b) // Exact multiply
.add(new BigDecimal((double)c))) // Exact sum
.floatValue(); // One rounding
// to a float value
}
} else {
// At least one of a,b, and c is non-finite. The result
// will be non-finite as well and will be the same
// non-finite value under double as float arithmetic.
return (float)fma((double)a, (double)b, (double)c);
}
}

/**
Expand Down
8 changes: 6 additions & 2 deletions test/jdk/java/lang/Math/FusedMultiplyAddTests.java
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -23,11 +23,12 @@

/*
* @test
* @bug 4851642
* @bug 4851642 8253409
* @summary Tests for Math.fusedMac and StrictMath.fusedMac.
* @build Tests
* @build FusedMultiplyAddTests
* @run main FusedMultiplyAddTests
* @run main/othervm -XX:-UseFMA FusedMultiplyAddTests
*/

/**
Expand Down Expand Up @@ -350,6 +351,9 @@ private static int testSimpleF() {

{1.0f+Math.ulp(1.0f), 1.0f+Math.ulp(1.0f), -1.0f-2.0f*Math.ulp(1.0f),
Math.ulp(1.0f)*Math.ulp(1.0f)},

// Double-rounding if done in double precision
{0x1.fffffep23f, 0x1.000004p28f, 0x1.fep5f, 0x1.000002p52f}
};

for (float[] testCase: testCases)
Expand Down