Conflict API definitions of Math.pow(x, 0.5) and Math.sqrt(x) for x={-0.0, Double.NEGATIVE_INFINITY}(Internet mail)

jiefu(傅杰) jiefu at tencent.com
Tue Apr 13 01:26:02 UTC 2021


Hi Joe,

Thanks for your nice sharing.

Very glad to know that the Java library implementation of pow does delegate to sqrt while respecting the relevant special cases.
This implementation [1] has set a good example for us which means it's safe to replace pow(x, 0.5) with sqrt(x) for all x > 0.0.

Thanks
Best regards,
Jie

[1] https://github.com/openjdk/jdk/blob/d84a7e55be40eae57b6c322694d55661a5053a55/src/java.base/share/classes/java/lang/FdLibm.java#L366


On 2021/4/13, 2:21 AM, "Joe Darcy" <joe.darcy at oracle.com> wrote:

    Hello,
    
    Adding some additional context, more recent versions of the IEEE 754 
    standard have given explicit recommendations for math library function 
    definitions, including pow. The Java definitions of those methods long 
    predate the IEEE 754 coverage and there are a small number of 
    differences, including in the pow special cases. These differences are 
    now described more explicitly in the JDK 17 specs as of
    
    JDK-8240632: Note differences between IEEE 754-2019 math lib special 
    cases and java.lang.Math
    
    The difference in question for pow is:
    
    >      * @apiNote
    >      * The special cases definitions of this method differ from the
    >      * special case definitions of the IEEE 754 recommended {@code
    >      * pow} operation for ±{@code 1.0} raised to an infinite
    >      * power. This method treats such cases as indeterminate and
    >      * specifies a NaN is returned. The IEEE 754 specification treats
    >      * the infinite power as a large integer (large-magnitude
    >      * floating-point numbers are numerically integers, specifically
    >      * even integers) and therefore specifies {@code 1.0} be returned.
    
    There are no plans to align the Java definition of pow with the IEEE 754 
    definition in these few cases.
    
    Note that the Java library implementation of pow does delegate to sqrt 
    while respecting the relevant special cases:
    
    >             } else if (y == 0.5) {
    >                 if (x >= -Double.MAX_VALUE) // Handle x == -infinity later
    >                     return Math.sqrt(x + 0.0); // Add 0.0 to properly 
    > handle x == -0.0
    https://github.com/openjdk/jdk/blob/d84a7e55be40eae57b6c322694d55661a5053a55/src/java.base/share/classes/java/lang/FdLibm.java#L366
    
    Mathematically, the sqrt function is about as pleasant a function as you 
    can be asked to approximate in floating-point arithmetic. The function 
    is smooth, doesn't overflow or underflow, and has a simple 
    Newton-iteration that can be expressed in basic arithmetic operations. 
    The pow function doesn't enjoy these properties and has a larger design 
    space for what a reasonable floating-point approximation could be.
    
    HTH,
    
    -Joe
    
    On 4/12/2021 6:39 AM, jiefu(傅杰) wrote:
    > Hi Raffaello,
    >
    > Thanks for your execllent analysis.
    > I agree with you now.
    > And I'll close the optimization PR [1] tomorrow if there is no objections.
    >
    > Thanks.
    > Best regards,
    > Jie
    >
    > [1] https://github.com/openjdk/jdk/pull/3404/
    >
    >
    > On 2021/4/12, 9:06 PM, "Raffaello Giulietti" <raffaello.giulietti at gmail.com> wrote:
    >
    >      Hi Jie,
    >      
    >      I don't think that changing the spec of Math.pow() to be misaligned with
    >      IEEE 754 would be a wise option. IEEE is much more pervasive than Java.
    >      There are many aspects in IEEE that might be seen as questionable, but
    >      at least it is a widely adopted standard.
    >      
    >      AFAIU, the only reason you would like to "optimize" the special case of
    >      y = 0.5 in pow(x, y) to return sqrt(x) is for performance, more accuracy
    >      and some kind of consistency.
    >      
    >      But then, why not a special case for y = 0.25 as sqrt(sqrt(x))?
    >      And what about y = 0.75? Should this be translated to sqrt(sqrt(pow(x, 3)))?
    >      What about y = 1.0 / 3.0? Should this become cbrt(x)?
    >      And why not consider y = 2.0 / 3.0 in a special rule: cbrt(x * x)?
    >      
    >      You see, the special cases can quickly become unmanageable. Also,
    >      special rules would produce results which are "discontinuous" with
    >      nearby exponents, like y = 0.5000000000000001.
    >      
    >      That's probably why IEEE doesn't propose translation rules for finite
    >      numerical exponents that are not integers, except when x is a special value.
    >      
    >      
    >      Greetings
    >      Raffaello
    >      
    >      
    >      
    >      On 2021-04-12 13:44, jiefu(傅杰) wrote:
    >      > Hi Andrew H, Andrew D, and Raffaello,
    >      >
    >      > Thank you all for your kind reply and helpful comments.
    >      >
    >      > Now I got where the rules come from.
    >      > But I don't think the IEEE standars are reasonable to specify conflits rules.
    >      > Maybe, these computations should be open to be implementation dependent.
    >      >
    >      > (If it's possible) I really hope the special cases of Math.pow(x, 0.5) can be aligned with Math.sqrt(x) in Java.
    >      > We already allow some plausible behaviors to be different with the IEEE recommendations for some special cases, right?
    >      > And in that case, we can replace pow(x, 0.5) with sqrt(x) safely.
    >      >
    >      > Thanks.
    >      > Best regards,
    >      > Jie
    >      >
    >      >
    >      > On 2021/4/12, 6:40 PM, "Raffaello Giulietti" <raffaello.giulietti at gmail.com> wrote:
    >      >
    >      >      Hi Jie,
    >      >
    >      >      the behavior you report is the one specified by the standard IEEE 754.
    >      >      Java follows this standard as closely as it can.
    >      >
    >      >      The standard says that
    >      >      * squareRoot(-0) = -0
    >      >      * squareRoot(-∞) = NaN
    >      >
    >      >      Also, the standard has a long lists of special cases for pow(x, y),
    >      >      among them:
    >      >      * pow(±0, y) is +0 for finite y > 0 and not an odd integer
    >      >      * pow(-∞, y) is +∞ for finite y > 0 and not an odd integer
    >      >
    >      >      Thus, the conflicts you observe originate in following the standard, not
    >      >      by special Java rules.
    >      >
    >      >      Unfortunately, the IEEE standard does not explain the reasons for the
    >      >      special rules. Some are obvious, some are not.
    >      >
    >      >
    >      >      HTH
    >      >      Raffaello
    >      >
    >      >
    >      >      > Hi all,
    >      >      >
    >      >      > I found Math.pow(x, 0.5) and Math.sqrt(x) would compute different values as the following:
    >      >      > ```
    >      >      > Math.pow(-0.0, 0.5) = 0.0
    >      >      > Math.sqrt(-0.0) = -0.0
    >      >      >
    >      >      > Math.pow(Double.NEGATIVE_INFINITY, 0.5) = Infinity
    >      >      > Math.sqrt(Double.NEGATIVE_INFINITY) = NaN
    >      >      > ```
    >      >      >
    >      >      > The reason is that both of pow and sqrt have special rules for these computations.
    >      >      > For example, this rule [1] specifies Math.pow(-0.0, 0.5) must be 0.0.
    >      >      > And this one [2] specifies Math.sqrt(-0.0) must be -0.0.
    >      >      > And we do have rules for Math.pow(Double.NEGATIVE_INFINITY, 0.5) = Infinity and Math.sqrt(Double.NEGATIVE_INFINITY) = NaN too.
    >      >      >
    >      >      > I think most people will be confused by these rules because from the view of mathematics, Math.pow(x, 0.5) should be equal to Math.sqrt(x).
    >      >      >
    >      >      > So why Java creates conflict special rules for them?
    >      >      > Is it possible to let Math.pow(-0.0, 0.5) = -0.0 and Math.pow(Double.NEGATIVE_INFINITY, 0.5) = NaN also be allowed?
    >      >      >
    >      >      > I came across this problem when I was trying to optimize pow(x, 0.5) with sqrt(x).
    >      >      > If pow(x, 0.5)'s two special rules can be aligned with sqrt(x), then pow(x, 0.5)'s performance can be improved by 7x~14x [3].
    >      >      >
    >      >      > Thanks.
    >      >      > Best regards,
    >      >      > Jie
    >      >
    >      >
    >      >
    >      
    >      
    >
    
    
    



More information about the hotspot-compiler-dev mailing list