aherbert commented on code in PR #232:
URL: https://github.com/apache/commons-math/pull/232#discussion_r1187930106


##########
commons-math-core/src/main/java/org/apache/commons/math4/core/jdkmath/AccurateMath.java:
##########
@@ -3077,164 +3077,242 @@ public static float ulp(float x) {
     }
 
     /**
-     * Multiply a double number by a power of 2.
-     * @param d number to multiply
-     * @param n power of 2
-     * @return d &times; 2<sup>n</sup>
+     * Compute <code>2<sup>scaleFactor</sup> &times; d</code>.
+     * 
+     * @param d <code>d</code>
+     * @param scaleFactor <code>scaleFactor</code>
+     * @return <code>2<sup>scaleFactor</sup> &times; d</code>
      */
-    public static double scalb(final double d, final int n) {
+    public static double scalb(final double d, final int scaleFactor) {
+
+        final int sizeBits = 64;
+        final int fractionBits = 52;
+        final int implicitFractionBits = fractionBits + 1;
+        final int maximumExponent = Double.MAX_EXPONENT;
+        final int minimumExponent = Double.MIN_EXPONENT;
+        final int minumumSubnormalExponent = Double.MIN_EXPONENT - 
fractionBits;
+        final int exponentBias = 1023;
+        
+        final long signMask = 0x8000000000000000L;
+        final long exponentMask = 0x7FF0000000000000L;
+        final long fractionMask = 0xFFFFFFFFFFFFFL;
+
+        // bit cast d. It is OK (and faster) to use raw because we will trap 
special values below.
+        final long bits = Double.doubleToRawLongBits(d);
 
-        // first simple and fast handling when 2^n can be represented using 
normal numbers
-        if ((n > -1023) && (n < 1024)) {
-            return d * Double.longBitsToDouble(((long) (n + 1023)) << 52);

Review Comment:
   By dropping this are we removing a faster code execution for this case? 
Decomposition of the input double is not required. I would think that a typical 
use of scalb is when you have a number that you think can be scaled to 
something useful (i.e. a normal to normal scaling). A non-typical use case 
would be to use scalb to create non-normal numbers. By this logic, this code 
path may be quite common in practice.
   
   Under your control flow the same case would have: a check for non-finite; a 
check for non-zero exponent and then the statement to check the result exponent 
is within the allowed range. For a majority use case all these branches may be 
very predictable. The speed is then the multiplication plus the composition of 
the factor (old method), verses the decomposition and then recomposition of the 
double. It may be interesting to measure the two codes with a JMH benchmark.



##########
commons-math-core/src/test/java/org/apache/commons/math4/core/jdkmath/ScalbTest.java:
##########
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.core.jdkmath;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests for scalb.
+ */
+public class ScalbTest {
+    
+    @Test
+    public void doubleSpecial() {
+        
+        double nonCanonicalNaN = Double.longBitsToDouble(0x7ff8000000000001L);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {            
+            assertBitPattern(StrictMath.scalb(Double.NaN, scaleFactor), 
AccurateMath.scalb(Double.NaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(nonCanonicalNaN, scaleFactor), 
AccurateMath.scalb(nonCanonicalNaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.POSITIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.POSITIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+        }
+    }
+      
+    @Test
+    public void doubleZero() {
+        
+        double zero = 0;
+        double negativeZero = Math.copySign(zero, -1d);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {   
+            assertBitPattern(StrictMath.scalb(zero, scaleFactor), 
AccurateMath.scalb(zero, scaleFactor));
+            assertBitPattern(StrictMath.scalb(negativeZero, scaleFactor), 
AccurateMath.scalb(negativeZero, scaleFactor));
+        }
+    }
+        
+    @Test
+    public void doubleNormalValues() {
+        
+        long m = 1;
+        for(int exp = Double.MIN_EXPONENT; exp <= Double.MAX_EXPONENT; exp++) {
+            
+            long exponentBits = (exp + 1023L) << 52;
+            double powerOfTwo = Double.longBitsToDouble(exponentBits);
+            
+            for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {
+                
+                // check the power of two case.
+                assertBitPattern(StrictMath.scalb(powerOfTwo, scaleFactor), 
AccurateMath.scalb(powerOfTwo, scaleFactor));
+                
+                for(int count = 0; count < 100; count++) {
+                    
+                    // generate mantissa and construct the value.
+                    long mantissaBits = m & 0x000FFFFFFFFFFFFFL;
+                    double x = Double.longBitsToDouble(exponentBits | 
mantissaBits);
+                    
+                    assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                    assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+                    
+                    // Marsaglia XOR pseudo-random shift.
+                    m ^= m << 13;
+                    m ^= m >> 17;
+                    m ^= m << 5;
+                }
+            }            
+        }
+    }
+    
+    @Test
+    public void doubleSubnormal() {
+           
+        long m = 1;
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {            
+            for(int count = 0; count < 50000; count++) {
+                
+                long mantissaBits = m & 0x000FFFFFFFFFFFFFL;           
+                double x = Double.longBitsToDouble(mantissaBits);
+                
+                Assert.assertTrue(x <= Double.MIN_NORMAL);
+                assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+
+                m ^= m << 13;
+                m ^= m >> 17;
+                m ^= m << 5;
+            }        
+        }        
+    }
+        
+    @Test
+    public void floatSpecial() {
+                
+        for(int scaleFactor = Float.MIN_EXPONENT * 2; scaleFactor <= 
(Float.MAX_EXPONENT - Float.MIN_EXPONENT); scaleFactor++) {            
+            assertBitPattern(StrictMath.scalb(Float.NaN, scaleFactor), 
AccurateMath.scalb(Float.NaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Float.POSITIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Float.POSITIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Float.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Float.NEGATIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Float.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Float.NEGATIVE_INFINITY, scaleFactor));
+        }
+    }
+      
+    @Test
+    public void floatZero() {
+        
+        float zero = 0;
+        float negativeZero = Math.copySign(zero, -1f);
+        
+        for(int scaleFactor = Float.MIN_EXPONENT * 2; scaleFactor <= 
(Float.MAX_EXPONENT - Float.MIN_EXPONENT); scaleFactor++) {   
+            assertBitPattern(StrictMath.scalb(zero, scaleFactor), 
AccurateMath.scalb(zero, scaleFactor));
+            assertBitPattern(StrictMath.scalb(negativeZero, scaleFactor), 
AccurateMath.scalb(negativeZero, scaleFactor));
+        }
+    }    
+   
+    @Test
+    public void floatNormalValues() {
+        
+        int m = 1;
+        for(int exp = Float.MIN_EXPONENT; exp <= Float.MAX_EXPONENT; exp++) {
+            
+            int exponentBits = (exp + 127) << 23;
+            float powerOfTwo = Float.intBitsToFloat(exponentBits);
+            
+            for(int scaleFactor = Float.MIN_EXPONENT * 2; scaleFactor <= 
(Float.MAX_EXPONENT - Float.MIN_EXPONENT); scaleFactor++) {
+                
+                // check the power of two case.
+                assertBitPattern(StrictMath.scalb(powerOfTwo, scaleFactor), 
AccurateMath.scalb(powerOfTwo, scaleFactor));
+                
+                for(int count = 0; count < 100; count++) {
+                    
+                    // generate mantissa and construct the value.
+                    int mantissaBits = m & 0x7fffff;
+                    float x = Float.intBitsToFloat(exponentBits | 
mantissaBits);
+                    
+                    assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                    assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+                    
+                    // Marsaglia XOR pseudo-random shift.
+                    m ^= m << 13;
+                    m ^= m >> 17;
+                    m ^= m << 5;
+                }
+            }            
+        }
+    }
+    
+    @Test
+    public void floatSubnormal() {
+           
+        int m = 1;
+        for(int scaleFactor = Float.MIN_EXPONENT * 2; scaleFactor <= 
(Float.MAX_EXPONENT - Float.MIN_EXPONENT); scaleFactor++) {            
+            for(int count = 0; count < 50000; count++) {
+                
+                int mantissaBits = m & 0x7fffff;               
+                float x = Float.intBitsToFloat(mantissaBits);
+                
+                Assert.assertTrue(x <= Float.MIN_NORMAL);
+                assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+
+                m ^= m << 13;

Review Comment:
   Replace with a RNG object



##########
commons-math-core/src/test/java/org/apache/commons/math4/core/jdkmath/ScalbTest.java:
##########
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.core.jdkmath;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests for scalb.
+ */
+public class ScalbTest {
+    
+    @Test
+    public void doubleSpecial() {
+        
+        double nonCanonicalNaN = Double.longBitsToDouble(0x7ff8000000000001L);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {            
+            assertBitPattern(StrictMath.scalb(Double.NaN, scaleFactor), 
AccurateMath.scalb(Double.NaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(nonCanonicalNaN, scaleFactor), 
AccurateMath.scalb(nonCanonicalNaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.POSITIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.POSITIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+        }
+    }
+      
+    @Test
+    public void doubleZero() {
+        
+        double zero = 0;
+        double negativeZero = Math.copySign(zero, -1d);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {   
+            assertBitPattern(StrictMath.scalb(zero, scaleFactor), 
AccurateMath.scalb(zero, scaleFactor));
+            assertBitPattern(StrictMath.scalb(negativeZero, scaleFactor), 
AccurateMath.scalb(negativeZero, scaleFactor));
+        }
+    }
+        
+    @Test
+    public void doubleNormalValues() {
+        
+        long m = 1;

Review Comment:
   Do not use an inline RNG. Just use a robust UniformRandomProvider from the 
Commons RNG project with some fixed seed, e.g. 
   ```
   UniformRandomProvider rng = 
RandomSource.XO_RO_SHI_RO_128_PP.create(0xdeadbeaf);
   ```



##########
commons-math-core/src/test/java/org/apache/commons/math4/core/jdkmath/ScalbTest.java:
##########
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.core.jdkmath;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests for scalb.
+ */
+public class ScalbTest {
+    
+    @Test
+    public void doubleSpecial() {
+        
+        double nonCanonicalNaN = Double.longBitsToDouble(0x7ff8000000000001L);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {            
+            assertBitPattern(StrictMath.scalb(Double.NaN, scaleFactor), 
AccurateMath.scalb(Double.NaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(nonCanonicalNaN, scaleFactor), 
AccurateMath.scalb(nonCanonicalNaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.POSITIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.POSITIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+        }
+    }
+      
+    @Test
+    public void doubleZero() {
+        
+        double zero = 0;
+        double negativeZero = Math.copySign(zero, -1d);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {   
+            assertBitPattern(StrictMath.scalb(zero, scaleFactor), 
AccurateMath.scalb(zero, scaleFactor));
+            assertBitPattern(StrictMath.scalb(negativeZero, scaleFactor), 
AccurateMath.scalb(negativeZero, scaleFactor));
+        }
+    }
+        
+    @Test
+    public void doubleNormalValues() {
+        
+        long m = 1;
+        for(int exp = Double.MIN_EXPONENT; exp <= Double.MAX_EXPONENT; exp++) {
+            
+            long exponentBits = (exp + 1023L) << 52;
+            double powerOfTwo = Double.longBitsToDouble(exponentBits);
+            
+            for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {
+                
+                // check the power of two case.
+                assertBitPattern(StrictMath.scalb(powerOfTwo, scaleFactor), 
AccurateMath.scalb(powerOfTwo, scaleFactor));
+                
+                for(int count = 0; count < 100; count++) {
+                    
+                    // generate mantissa and construct the value.
+                    long mantissaBits = m & 0x000FFFFFFFFFFFFFL;
+                    double x = Double.longBitsToDouble(exponentBits | 
mantissaBits);
+                    
+                    assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                    assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+                    
+                    // Marsaglia XOR pseudo-random shift.
+                    m ^= m << 13;
+                    m ^= m >> 17;
+                    m ^= m << 5;
+                }
+            }            
+        }
+    }
+    
+    @Test
+    public void doubleSubnormal() {
+           
+        long m = 1;
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {            
+            for(int count = 0; count < 50000; count++) {
+                
+                long mantissaBits = m & 0x000FFFFFFFFFFFFFL;           
+                double x = Double.longBitsToDouble(mantissaBits);
+                
+                Assert.assertTrue(x <= Double.MIN_NORMAL);
+                assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+
+                m ^= m << 13;
+                m ^= m >> 17;
+                m ^= m << 5;
+            }        
+        }        
+    }
+        
+    @Test
+    public void floatSpecial() {
+                
+        for(int scaleFactor = Float.MIN_EXPONENT * 2; scaleFactor <= 
(Float.MAX_EXPONENT - Float.MIN_EXPONENT); scaleFactor++) {            
+            assertBitPattern(StrictMath.scalb(Float.NaN, scaleFactor), 
AccurateMath.scalb(Float.NaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Float.POSITIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Float.POSITIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Float.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Float.NEGATIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Float.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Float.NEGATIVE_INFINITY, scaleFactor));
+        }
+    }
+      
+    @Test
+    public void floatZero() {
+        
+        float zero = 0;
+        float negativeZero = Math.copySign(zero, -1f);
+        
+        for(int scaleFactor = Float.MIN_EXPONENT * 2; scaleFactor <= 
(Float.MAX_EXPONENT - Float.MIN_EXPONENT); scaleFactor++) {   
+            assertBitPattern(StrictMath.scalb(zero, scaleFactor), 
AccurateMath.scalb(zero, scaleFactor));
+            assertBitPattern(StrictMath.scalb(negativeZero, scaleFactor), 
AccurateMath.scalb(negativeZero, scaleFactor));
+        }
+    }    
+   
+    @Test
+    public void floatNormalValues() {
+        
+        int m = 1;
+        for(int exp = Float.MIN_EXPONENT; exp <= Float.MAX_EXPONENT; exp++) {
+            
+            int exponentBits = (exp + 127) << 23;
+            float powerOfTwo = Float.intBitsToFloat(exponentBits);
+            
+            for(int scaleFactor = Float.MIN_EXPONENT * 2; scaleFactor <= 
(Float.MAX_EXPONENT - Float.MIN_EXPONENT); scaleFactor++) {
+                
+                // check the power of two case.
+                assertBitPattern(StrictMath.scalb(powerOfTwo, scaleFactor), 
AccurateMath.scalb(powerOfTwo, scaleFactor));
+                
+                for(int count = 0; count < 100; count++) {
+                    
+                    // generate mantissa and construct the value.
+                    int mantissaBits = m & 0x7fffff;
+                    float x = Float.intBitsToFloat(exponentBits | 
mantissaBits);
+                    
+                    assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                    assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+                    
+                    // Marsaglia XOR pseudo-random shift.
+                    m ^= m << 13;
+                    m ^= m >> 17;
+                    m ^= m << 5;
+                }
+            }            
+        }
+    }
+    
+    @Test
+    public void floatSubnormal() {
+           
+        int m = 1;
+        for(int scaleFactor = Float.MIN_EXPONENT * 2; scaleFactor <= 
(Float.MAX_EXPONENT - Float.MIN_EXPONENT); scaleFactor++) {            
+            for(int count = 0; count < 50000; count++) {
+                
+                int mantissaBits = m & 0x7fffff;               
+                float x = Float.intBitsToFloat(mantissaBits);
+                
+                Assert.assertTrue(x <= Float.MIN_NORMAL);
+                assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+
+                m ^= m << 13;
+                m ^= m >> 17;
+                m ^= m << 5;
+            }        
+        }        
+    }
+    
+    /**
+     * Assert that the specified doubles have the same bit pattern, except for 
NaN.
+     * <p>
+     * If <code>expected</code> is NaN, we only check that <code>actual</code> 
is also
+     * NaN, not that they have the same fraction value.
+     * 
+     * @param expected Expected value.
+     * @param actual Actual value.
+     */
+    private static void assertBitPattern(final double expected, final double 
actual) {
+        
+        if(Double.isNaN(expected)) { 
+            Assert.assertTrue(Double.isNaN(actual));
+            return;
+        }
+                
+        Assert.assertEquals(Double.doubleToRawLongBits(expected), 
Double.doubleToRawLongBits(actual));
+    }
+    
+    /**
+     * Assert that the specified floats have the same bit pattern, except for 
NaN.
+     * <p>
+     * If <code>expected</code> is NaN, we only check that <code>actual</code> 
is also
+     * NaN, not that they have the same fraction value.
+     * 
+     * @param expected Expected value.
+     * @param actual Actual value.
+     */
+    private static void assertBitPattern(final float expected, final float 
actual) {
+        
+        if(Float.isNaN(expected)) { 
+            Assert.assertTrue(Float.isNaN(actual));
+            return;
+        }
+                
+        Assert.assertEquals(Float.floatToIntBits(expected), 
Float.floatToIntBits(actual));
+    }
+

Review Comment:
   Remove extra new line



##########
commons-math-core/src/test/java/org/apache/commons/math4/core/jdkmath/ScalbTest.java:
##########
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.core.jdkmath;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests for scalb.
+ */
+public class ScalbTest {

Review Comment:
   The current AccurateMath tests current reside in `commons-math-legacy-core` 
module (due to an artefact of moving around classes for the module structure). 
For now I would move this to 
`org.apache.commons.math4.legacy.core.jdkmath.AccurateMathScalbTest` in that 
module so all the AccurateMath tests are in the same place.



##########
commons-math-core/src/test/java/org/apache/commons/math4/core/jdkmath/ScalbTest.java:
##########
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.core.jdkmath;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests for scalb.
+ */
+public class ScalbTest {
+    
+    @Test
+    public void doubleSpecial() {
+        
+        double nonCanonicalNaN = Double.longBitsToDouble(0x7ff8000000000001L);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {            
+            assertBitPattern(StrictMath.scalb(Double.NaN, scaleFactor), 
AccurateMath.scalb(Double.NaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(nonCanonicalNaN, scaleFactor), 
AccurateMath.scalb(nonCanonicalNaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.POSITIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.POSITIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+        }
+    }
+      
+    @Test
+    public void doubleZero() {
+        
+        double zero = 0;
+        double negativeZero = Math.copySign(zero, -1d);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {   
+            assertBitPattern(StrictMath.scalb(zero, scaleFactor), 
AccurateMath.scalb(zero, scaleFactor));
+            assertBitPattern(StrictMath.scalb(negativeZero, scaleFactor), 
AccurateMath.scalb(negativeZero, scaleFactor));
+        }
+    }
+        
+    @Test
+    public void doubleNormalValues() {
+        
+        long m = 1;
+        for(int exp = Double.MIN_EXPONENT; exp <= Double.MAX_EXPONENT; exp++) {
+            
+            long exponentBits = (exp + 1023L) << 52;
+            double powerOfTwo = Double.longBitsToDouble(exponentBits);
+            
+            for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {
+                
+                // check the power of two case.
+                assertBitPattern(StrictMath.scalb(powerOfTwo, scaleFactor), 
AccurateMath.scalb(powerOfTwo, scaleFactor));
+                
+                for(int count = 0; count < 100; count++) {
+                    
+                    // generate mantissa and construct the value.
+                    long mantissaBits = m & 0x000FFFFFFFFFFFFFL;
+                    double x = Double.longBitsToDouble(exponentBits | 
mantissaBits);
+                    
+                    assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                    assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+                    
+                    // Marsaglia XOR pseudo-random shift.
+                    m ^= m << 13;
+                    m ^= m >> 17;
+                    m ^= m << 5;
+                }
+            }            
+        }
+    }
+    
+    @Test
+    public void doubleSubnormal() {
+           
+        long m = 1;
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {            
+            for(int count = 0; count < 50000; count++) {
+                
+                long mantissaBits = m & 0x000FFFFFFFFFFFFFL;           
+                double x = Double.longBitsToDouble(mantissaBits);
+                
+                Assert.assertTrue(x <= Double.MIN_NORMAL);
+                assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+
+                m ^= m << 13;

Review Comment:
   Remove



##########
commons-math-core/src/test/java/org/apache/commons/math4/core/jdkmath/ScalbTest.java:
##########
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.core.jdkmath;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests for scalb.
+ */
+public class ScalbTest {
+    
+    @Test
+    public void doubleSpecial() {
+        
+        double nonCanonicalNaN = Double.longBitsToDouble(0x7ff8000000000001L);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {            
+            assertBitPattern(StrictMath.scalb(Double.NaN, scaleFactor), 
AccurateMath.scalb(Double.NaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(nonCanonicalNaN, scaleFactor), 
AccurateMath.scalb(nonCanonicalNaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.POSITIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.POSITIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+        }
+    }
+      
+    @Test
+    public void doubleZero() {
+        
+        double zero = 0;
+        double negativeZero = Math.copySign(zero, -1d);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {   
+            assertBitPattern(StrictMath.scalb(zero, scaleFactor), 
AccurateMath.scalb(zero, scaleFactor));
+            assertBitPattern(StrictMath.scalb(negativeZero, scaleFactor), 
AccurateMath.scalb(negativeZero, scaleFactor));
+        }
+    }
+        
+    @Test
+    public void doubleNormalValues() {
+        
+        long m = 1;
+        for(int exp = Double.MIN_EXPONENT; exp <= Double.MAX_EXPONENT; exp++) {
+            
+            long exponentBits = (exp + 1023L) << 52;
+            double powerOfTwo = Double.longBitsToDouble(exponentBits);
+            
+            for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {
+                
+                // check the power of two case.
+                assertBitPattern(StrictMath.scalb(powerOfTwo, scaleFactor), 
AccurateMath.scalb(powerOfTwo, scaleFactor));
+                
+                for(int count = 0; count < 100; count++) {
+                    
+                    // generate mantissa and construct the value.
+                    long mantissaBits = m & 0x000FFFFFFFFFFFFFL;
+                    double x = Double.longBitsToDouble(exponentBits | 
mantissaBits);
+                    
+                    assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                    assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+                    
+                    // Marsaglia XOR pseudo-random shift.
+                    m ^= m << 13;
+                    m ^= m >> 17;
+                    m ^= m << 5;
+                }
+            }            
+        }
+    }
+    
+    @Test
+    public void doubleSubnormal() {
+           
+        long m = 1;
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {            
+            for(int count = 0; count < 50000; count++) {
+                
+                long mantissaBits = m & 0x000FFFFFFFFFFFFFL;           
+                double x = Double.longBitsToDouble(mantissaBits);
+                
+                Assert.assertTrue(x <= Double.MIN_NORMAL);
+                assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+
+                m ^= m << 13;
+                m ^= m >> 17;
+                m ^= m << 5;
+            }        
+        }        
+    }
+        
+    @Test
+    public void floatSpecial() {
+                

Review Comment:
   Remove extra new line



##########
commons-math-core/src/test/java/org/apache/commons/math4/core/jdkmath/ScalbTest.java:
##########
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.core.jdkmath;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests for scalb.
+ */
+public class ScalbTest {
+    
+    @Test
+    public void doubleSpecial() {
+        
+        double nonCanonicalNaN = Double.longBitsToDouble(0x7ff8000000000001L);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {            
+            assertBitPattern(StrictMath.scalb(Double.NaN, scaleFactor), 
AccurateMath.scalb(Double.NaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(nonCanonicalNaN, scaleFactor), 
AccurateMath.scalb(nonCanonicalNaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.POSITIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.POSITIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+        }
+    }
+      
+    @Test
+    public void doubleZero() {
+        
+        double zero = 0;
+        double negativeZero = Math.copySign(zero, -1d);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {   
+            assertBitPattern(StrictMath.scalb(zero, scaleFactor), 
AccurateMath.scalb(zero, scaleFactor));
+            assertBitPattern(StrictMath.scalb(negativeZero, scaleFactor), 
AccurateMath.scalb(negativeZero, scaleFactor));
+        }
+    }
+        
+    @Test
+    public void doubleNormalValues() {
+        
+        long m = 1;
+        for(int exp = Double.MIN_EXPONENT; exp <= Double.MAX_EXPONENT; exp++) {
+            
+            long exponentBits = (exp + 1023L) << 52;
+            double powerOfTwo = Double.longBitsToDouble(exponentBits);
+            
+            for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {
+                
+                // check the power of two case.
+                assertBitPattern(StrictMath.scalb(powerOfTwo, scaleFactor), 
AccurateMath.scalb(powerOfTwo, scaleFactor));
+                
+                for(int count = 0; count < 100; count++) {
+                    
+                    // generate mantissa and construct the value.
+                    long mantissaBits = m & 0x000FFFFFFFFFFFFFL;
+                    double x = Double.longBitsToDouble(exponentBits | 
mantissaBits);
+                    
+                    assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                    assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+                    
+                    // Marsaglia XOR pseudo-random shift.
+                    m ^= m << 13;
+                    m ^= m >> 17;
+                    m ^= m << 5;
+                }
+            }            
+        }
+    }
+    
+    @Test
+    public void doubleSubnormal() {
+           
+        long m = 1;
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {            
+            for(int count = 0; count < 50000; count++) {

Review Comment:
   This is a lot of repeats. But you do not include the explicit case in 
MATH-1660 when rounding has sticky bits 0. I would add some explicit tests to 
ensure code paths are hit without requiring a lot of randomness. Something like 
this should hit cases of round-to-even with a trailing guard bit and further 
'sticky' bit:
   
   ```Java
   @ParameterizedTest
   @MethodSource
   void testScalb(double x, int n) {
       Assertions.assertEquals(StrictMath.scalb(x, n), AccurateMath.scalb(x, 
n));
       Assertions.assertEquals(StrictMath.scalb(-x, n), AccurateMath.scalb(-x, 
n));
   }
   
   static Stream<Arguments> testScalb() {
       Stream.Builder<Arguments> builder = Stream.builder();
       builder.add(Arguments.of(-0x1.8df4a353af3a8p-2, -1024));
       for (int i = 0; i < 8; i++) {
           builder.add(Arguments.of(Double.longBitsToDouble(511L << 52 | i), 
-512));
       }
       for (int i = 0; i < 8; i++) {
           builder.add(Arguments.of(Double.longBitsToDouble(1023L << 52 | i), 
-1024));
       }
       return builder.build();
   }
   ``` 
   
   The old CM code fails on `1023L << 52 | 2`.
   
   Note that you can call `assertScalb` from other methods too as this can 
become your default assertion method, e.g. in `doubleSpecial` do 
`assertScalb(Double.NaN, scaleFactor);` etc.
   



##########
commons-math-core/src/test/java/org/apache/commons/math4/core/jdkmath/ScalbTest.java:
##########
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.core.jdkmath;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests for scalb.
+ */
+public class ScalbTest {
+    
+    @Test
+    public void doubleSpecial() {
+        
+        double nonCanonicalNaN = Double.longBitsToDouble(0x7ff8000000000001L);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {            
+            assertBitPattern(StrictMath.scalb(Double.NaN, scaleFactor), 
AccurateMath.scalb(Double.NaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(nonCanonicalNaN, scaleFactor), 
AccurateMath.scalb(nonCanonicalNaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.POSITIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.POSITIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+        }
+    }
+      
+    @Test
+    public void doubleZero() {
+        
+        double zero = 0;
+        double negativeZero = Math.copySign(zero, -1d);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {   
+            assertBitPattern(StrictMath.scalb(zero, scaleFactor), 
AccurateMath.scalb(zero, scaleFactor));
+            assertBitPattern(StrictMath.scalb(negativeZero, scaleFactor), 
AccurateMath.scalb(negativeZero, scaleFactor));
+        }
+    }
+        
+    @Test
+    public void doubleNormalValues() {
+        
+        long m = 1;
+        for(int exp = Double.MIN_EXPONENT; exp <= Double.MAX_EXPONENT; exp++) {
+            
+            long exponentBits = (exp + 1023L) << 52;
+            double powerOfTwo = Double.longBitsToDouble(exponentBits);
+            
+            for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {
+                
+                // check the power of two case.
+                assertBitPattern(StrictMath.scalb(powerOfTwo, scaleFactor), 
AccurateMath.scalb(powerOfTwo, scaleFactor));
+                
+                for(int count = 0; count < 100; count++) {
+                    
+                    // generate mantissa and construct the value.
+                    long mantissaBits = m & 0x000FFFFFFFFFFFFFL;
+                    double x = Double.longBitsToDouble(exponentBits | 
mantissaBits);
+                    
+                    assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                    assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+                    
+                    // Marsaglia XOR pseudo-random shift.
+                    m ^= m << 13;
+                    m ^= m >> 17;
+                    m ^= m << 5;
+                }
+            }            
+        }
+    }
+    
+    @Test
+    public void doubleSubnormal() {
+           
+        long m = 1;
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {            
+            for(int count = 0; count < 50000; count++) {
+                
+                long mantissaBits = m & 0x000FFFFFFFFFFFFFL;           
+                double x = Double.longBitsToDouble(mantissaBits);
+                
+                Assert.assertTrue(x <= Double.MIN_NORMAL);
+                assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+
+                m ^= m << 13;
+                m ^= m >> 17;
+                m ^= m << 5;
+            }        
+        }        
+    }
+        
+    @Test
+    public void floatSpecial() {
+                
+        for(int scaleFactor = Float.MIN_EXPONENT * 2; scaleFactor <= 
(Float.MAX_EXPONENT - Float.MIN_EXPONENT); scaleFactor++) {            
+            assertBitPattern(StrictMath.scalb(Float.NaN, scaleFactor), 
AccurateMath.scalb(Float.NaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Float.POSITIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Float.POSITIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Float.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Float.NEGATIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Float.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Float.NEGATIVE_INFINITY, scaleFactor));
+        }
+    }
+      
+    @Test
+    public void floatZero() {
+        
+        float zero = 0;
+        float negativeZero = Math.copySign(zero, -1f);
+        
+        for(int scaleFactor = Float.MIN_EXPONENT * 2; scaleFactor <= 
(Float.MAX_EXPONENT - Float.MIN_EXPONENT); scaleFactor++) {   
+            assertBitPattern(StrictMath.scalb(zero, scaleFactor), 
AccurateMath.scalb(zero, scaleFactor));
+            assertBitPattern(StrictMath.scalb(negativeZero, scaleFactor), 
AccurateMath.scalb(negativeZero, scaleFactor));
+        }
+    }    
+   
+    @Test
+    public void floatNormalValues() {
+        
+        int m = 1;
+        for(int exp = Float.MIN_EXPONENT; exp <= Float.MAX_EXPONENT; exp++) {
+            
+            int exponentBits = (exp + 127) << 23;
+            float powerOfTwo = Float.intBitsToFloat(exponentBits);
+            
+            for(int scaleFactor = Float.MIN_EXPONENT * 2; scaleFactor <= 
(Float.MAX_EXPONENT - Float.MIN_EXPONENT); scaleFactor++) {
+                
+                // check the power of two case.
+                assertBitPattern(StrictMath.scalb(powerOfTwo, scaleFactor), 
AccurateMath.scalb(powerOfTwo, scaleFactor));
+                
+                for(int count = 0; count < 100; count++) {
+                    
+                    // generate mantissa and construct the value.
+                    int mantissaBits = m & 0x7fffff;
+                    float x = Float.intBitsToFloat(exponentBits | 
mantissaBits);
+                    
+                    assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                    assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+                    
+                    // Marsaglia XOR pseudo-random shift.

Review Comment:
   Replace with a RNG object



##########
commons-math-core/src/test/java/org/apache/commons/math4/core/jdkmath/ScalbTest.java:
##########
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.math4.core.jdkmath;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests for scalb.
+ */
+public class ScalbTest {
+    
+    @Test
+    public void doubleSpecial() {
+        
+        double nonCanonicalNaN = Double.longBitsToDouble(0x7ff8000000000001L);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {            
+            assertBitPattern(StrictMath.scalb(Double.NaN, scaleFactor), 
AccurateMath.scalb(Double.NaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(nonCanonicalNaN, scaleFactor), 
AccurateMath.scalb(nonCanonicalNaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.POSITIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.POSITIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Double.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Double.NEGATIVE_INFINITY, scaleFactor));
+        }
+    }
+      
+    @Test
+    public void doubleZero() {
+        
+        double zero = 0;
+        double negativeZero = Math.copySign(zero, -1d);
+        
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {   
+            assertBitPattern(StrictMath.scalb(zero, scaleFactor), 
AccurateMath.scalb(zero, scaleFactor));
+            assertBitPattern(StrictMath.scalb(negativeZero, scaleFactor), 
AccurateMath.scalb(negativeZero, scaleFactor));
+        }
+    }
+        
+    @Test
+    public void doubleNormalValues() {
+        
+        long m = 1;
+        for(int exp = Double.MIN_EXPONENT; exp <= Double.MAX_EXPONENT; exp++) {
+            
+            long exponentBits = (exp + 1023L) << 52;
+            double powerOfTwo = Double.longBitsToDouble(exponentBits);
+            
+            for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {
+                
+                // check the power of two case.
+                assertBitPattern(StrictMath.scalb(powerOfTwo, scaleFactor), 
AccurateMath.scalb(powerOfTwo, scaleFactor));
+                
+                for(int count = 0; count < 100; count++) {
+                    
+                    // generate mantissa and construct the value.
+                    long mantissaBits = m & 0x000FFFFFFFFFFFFFL;
+                    double x = Double.longBitsToDouble(exponentBits | 
mantissaBits);
+                    
+                    assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                    assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+                    
+                    // Marsaglia XOR pseudo-random shift.
+                    m ^= m << 13;
+                    m ^= m >> 17;
+                    m ^= m << 5;
+                }
+            }            
+        }
+    }
+    
+    @Test
+    public void doubleSubnormal() {
+           
+        long m = 1;
+        for(int scaleFactor = Double.MIN_EXPONENT * 2; scaleFactor <= 
(Double.MAX_EXPONENT - Double.MIN_EXPONENT); scaleFactor++) {            
+            for(int count = 0; count < 50000; count++) {
+                
+                long mantissaBits = m & 0x000FFFFFFFFFFFFFL;           
+                double x = Double.longBitsToDouble(mantissaBits);
+                
+                Assert.assertTrue(x <= Double.MIN_NORMAL);
+                assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+
+                m ^= m << 13;
+                m ^= m >> 17;
+                m ^= m << 5;
+            }        
+        }        
+    }
+        
+    @Test
+    public void floatSpecial() {
+                
+        for(int scaleFactor = Float.MIN_EXPONENT * 2; scaleFactor <= 
(Float.MAX_EXPONENT - Float.MIN_EXPONENT); scaleFactor++) {            
+            assertBitPattern(StrictMath.scalb(Float.NaN, scaleFactor), 
AccurateMath.scalb(Float.NaN, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Float.POSITIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Float.POSITIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Float.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Float.NEGATIVE_INFINITY, scaleFactor));
+            assertBitPattern(StrictMath.scalb(Float.NEGATIVE_INFINITY, 
scaleFactor), AccurateMath.scalb(Float.NEGATIVE_INFINITY, scaleFactor));
+        }
+    }
+      
+    @Test
+    public void floatZero() {
+        
+        float zero = 0;
+        float negativeZero = Math.copySign(zero, -1f);
+        
+        for(int scaleFactor = Float.MIN_EXPONENT * 2; scaleFactor <= 
(Float.MAX_EXPONENT - Float.MIN_EXPONENT); scaleFactor++) {   
+            assertBitPattern(StrictMath.scalb(zero, scaleFactor), 
AccurateMath.scalb(zero, scaleFactor));
+            assertBitPattern(StrictMath.scalb(negativeZero, scaleFactor), 
AccurateMath.scalb(negativeZero, scaleFactor));
+        }
+    }    
+   
+    @Test
+    public void floatNormalValues() {
+        
+        int m = 1;
+        for(int exp = Float.MIN_EXPONENT; exp <= Float.MAX_EXPONENT; exp++) {
+            
+            int exponentBits = (exp + 127) << 23;
+            float powerOfTwo = Float.intBitsToFloat(exponentBits);
+            
+            for(int scaleFactor = Float.MIN_EXPONENT * 2; scaleFactor <= 
(Float.MAX_EXPONENT - Float.MIN_EXPONENT); scaleFactor++) {
+                
+                // check the power of two case.
+                assertBitPattern(StrictMath.scalb(powerOfTwo, scaleFactor), 
AccurateMath.scalb(powerOfTwo, scaleFactor));
+                
+                for(int count = 0; count < 100; count++) {
+                    
+                    // generate mantissa and construct the value.
+                    int mantissaBits = m & 0x7fffff;
+                    float x = Float.intBitsToFloat(exponentBits | 
mantissaBits);
+                    
+                    assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                    assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+                    
+                    // Marsaglia XOR pseudo-random shift.
+                    m ^= m << 13;
+                    m ^= m >> 17;
+                    m ^= m << 5;
+                }
+            }            
+        }
+    }
+    
+    @Test
+    public void floatSubnormal() {
+           
+        int m = 1;
+        for(int scaleFactor = Float.MIN_EXPONENT * 2; scaleFactor <= 
(Float.MAX_EXPONENT - Float.MIN_EXPONENT); scaleFactor++) {            
+            for(int count = 0; count < 50000; count++) {
+                
+                int mantissaBits = m & 0x7fffff;               
+                float x = Float.intBitsToFloat(mantissaBits);
+                
+                Assert.assertTrue(x <= Float.MIN_NORMAL);
+                assertBitPattern(StrictMath.scalb(x, scaleFactor), 
AccurateMath.scalb(x, scaleFactor));
+                assertBitPattern(StrictMath.scalb(-x, scaleFactor), 
AccurateMath.scalb(-x, scaleFactor));
+
+                m ^= m << 13;
+                m ^= m >> 17;
+                m ^= m << 5;
+            }        
+        }        
+    }
+    
+    /**
+     * Assert that the specified doubles have the same bit pattern, except for 
NaN.
+     * <p>
+     * If <code>expected</code> is NaN, we only check that <code>actual</code> 
is also
+     * NaN, not that they have the same fraction value.
+     * 
+     * @param expected Expected value.
+     * @param actual Actual value.
+     */
+    private static void assertBitPattern(final double expected, final double 
actual) {

Review Comment:
   This is not required. Using JUnit 5 this binary equality is performed by 
`Assertions.assertEquals`. Internally it just converts the double to a long 
using `doubleToLongBits` which collates all NaNs to a single long value.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to