From c1a1b6934af268c6a9d7d70ac1d593ad24355336 Mon Sep 17 00:00:00 2001 From: 0PrashantYadav0 Date: Tue, 20 Oct 2026 18:47:04 +0530 Subject: [PATCH] fix: avoid premature overflow in `math/base/special/coshf` and `sinhf` --- type: pre_commit_static_analysis_report description: Results of running static analysis checks when committing changes. report: - task: lint_filenames status: passed - task: lint_editorconfig status: passed - task: lint_markdown_pkg_readmes status: na - task: lint_markdown_docs status: na - task: lint_markdown status: na - task: lint_package_json status: na - task: lint_repl_help status: na - task: lint_javascript_src status: passed - task: lint_javascript_cli status: na - task: lint_javascript_examples status: na - task: lint_javascript_tests status: passed - task: lint_javascript_benchmarks status: na - task: lint_python status: na - task: lint_r status: na - task: lint_c_src status: missing_dependencies - task: lint_c_examples status: na - task: lint_c_benchmarks status: na - task: lint_c_tests_fixtures status: na - task: lint_shell status: na - task: lint_typescript_declarations status: passed - task: lint_typescript_tests status: na - task: lint_license_headers status: passed --- --- .../math/base/special/coshf/lib/main.js | 10 +++++-- .../math/base/special/coshf/src/main.c | 7 ++++- .../math/base/special/coshf/test/test.js | 29 ++++++++++++++++--- .../base/special/coshf/test/test.native.js | 29 ++++++++++++++++--- .../math/base/special/sinhf/lib/main.js | 14 ++++----- .../math/base/special/sinhf/manifest.json | 6 ---- .../math/base/special/sinhf/src/main.c | 14 ++++----- .../math/base/special/sinhf/test/test.js | 29 ++++++++++++++++--- .../base/special/sinhf/test/test.native.js | 29 ++++++++++++++++--- 9 files changed, 128 insertions(+), 39 deletions(-) diff --git a/lib/node_modules/@stdlib/math/base/special/coshf/lib/main.js b/lib/node_modules/@stdlib/math/base/special/coshf/lib/main.js index 3083259f1cad..519eb642b85a 100644 --- a/lib/node_modules/@stdlib/math/base/special/coshf/lib/main.js +++ b/lib/node_modules/@stdlib/math/base/special/coshf/lib/main.js @@ -45,6 +45,7 @@ var MAXLOGF = require( '@stdlib/constants/float32/max-ln' ); // VARIABLES // var ZERO = f32( 0.0 ); +var HALF = f32( 0.5 ); var ONE = f32( 1.0 ); var TWO = f32( 2.0 ); @@ -95,10 +96,15 @@ function coshf( x ) { if ( x < ZERO ) { x = -x; } - // Handle infinities & overflows: - if ( isInfinitef( x ) || x > MAXLOGF ) { + // Handle infinities: + if ( isInfinitef( x ) ) { return PINF; } + // Avoid premature overflow by evaluating `exp(x/2) * exp(x/2) / 2`: + if ( x >= MAXLOGF ) { + z = f32( exp( f32( HALF * x ) ) ); + return f32( f32( HALF * z ) * z ); + } z = f32( exp( f32( x ) ) ); z = f32( z + ( ONE/z ) ); return f32( z / TWO ); diff --git a/lib/node_modules/@stdlib/math/base/special/coshf/src/main.c b/lib/node_modules/@stdlib/math/base/special/coshf/src/main.c index 866ef5bea037..43e80b1fa81a 100644 --- a/lib/node_modules/@stdlib/math/base/special/coshf/src/main.c +++ b/lib/node_modules/@stdlib/math/base/special/coshf/src/main.c @@ -77,9 +77,14 @@ float stdlib_base_coshf( const float x ) { } else { z = x; } - if ( stdlib_base_is_infinitef( z ) || z > MAXLOGF ) { + if ( stdlib_base_is_infinitef( z ) ) { return STDLIB_CONSTANT_FLOAT32_PINF; } + // Avoid premature overflow by evaluating `exp(x/2) * exp(x/2) / 2`: + if ( z >= MAXLOGF ) { + z = expf( 0.5f * z ); + return ( 0.5f * z ) * z; + } z = expf( z ); z = z + ( 1.0f/z ); return ( z / 2.0f ); diff --git a/lib/node_modules/@stdlib/math/base/special/coshf/test/test.js b/lib/node_modules/@stdlib/math/base/special/coshf/test/test.js index 9694342ca703..7bb907f472d4 100644 --- a/lib/node_modules/@stdlib/math/base/special/coshf/test/test.js +++ b/lib/node_modules/@stdlib/math/base/special/coshf/test/test.js @@ -130,28 +130,49 @@ tape( 'the function returns `+infinity` if provided a `-infinity`', function tes t.end(); }); -tape( 'the function returns `+infinity` if provided a value greater than `MAXLOGF`', function test( t ) { +tape( 'the function returns `+infinity` if provided a value greater than `MAXLOGF + 1`', function test( t ) { var v; var i; var y; for ( i = 0; i < 500; i++ ) { - v = f32( ( randu()*1e3 ) + MAXLOGF ); + v = f32( ( randu()*1e3 ) + MAXLOGF + 1.0 ); y = coshf( v ); t.strictEqual( y, PINF, 'returns expected value' ); } t.end(); }); -tape( 'the function returns `+infinity` if provided a value less than `-MAXLOGF`', function test( t ) { +tape( 'the function returns `+infinity` if provided a value less than `-MAXLOGF - 1`', function test( t ) { var y; var v; var i; for ( i = 0; i < 500; i++ ) { - v = f32( ( -randu()*1e3 ) - MAXLOGF ); + v = f32( ( -randu()*1e3 ) - MAXLOGF - 1.0 ); y = coshf( v ); t.strictEqual( y, PINF, 'returns expected value' ); } t.end(); }); + +tape( 'the function does not overflow for values whose hyperbolic cosine is representable', function test( t ) { + var expected; + var x; + var v; + var i; + + x = [ 89.0, 89.2, 89.4 ]; + expected = [ + 2.2448063889081578e+38, + 2.741804322770982e+38, + 3.3488627241070614e+38 + ]; + for ( i = 0; i < x.length; i++ ) { + v = coshf( f32( x[ i ] ) ); + t.strictEqual( isAlmostSameValue( v, expected[ i ], 2 ), true, 'returns expected value' ); + v = coshf( f32( -x[ i ] ) ); + t.strictEqual( isAlmostSameValue( v, expected[ i ], 2 ), true, 'returns expected value' ); + } + t.end(); +}); diff --git a/lib/node_modules/@stdlib/math/base/special/coshf/test/test.native.js b/lib/node_modules/@stdlib/math/base/special/coshf/test/test.native.js index e57052c122b7..cf636f9c0c64 100644 --- a/lib/node_modules/@stdlib/math/base/special/coshf/test/test.native.js +++ b/lib/node_modules/@stdlib/math/base/special/coshf/test/test.native.js @@ -139,28 +139,49 @@ tape( 'the function returns `+infinity` if provided a `-infinity`', opts, functi t.end(); }); -tape( 'the function returns `+infinity` if provided a value greater than `MAXLOGF`', opts, function test( t ) { +tape( 'the function returns `+infinity` if provided a value greater than `MAXLOGF + 1`', opts, function test( t ) { var v; var i; var y; for ( i = 0; i < 500; i++ ) { - v = f32( ( randu()*1e3 ) + MAXLOGF ); + v = f32( ( randu()*1e3 ) + MAXLOGF + 1.0 ); y = coshf( v ); t.strictEqual( y, PINF, 'returns expected value' ); } t.end(); }); -tape( 'the function returns `+infinity` if provided a value less than `-MAXLOGF`', opts, function test( t ) { +tape( 'the function returns `+infinity` if provided a value less than `-MAXLOGF - 1`', opts, function test( t ) { var y; var v; var i; for ( i = 0; i < 500; i++ ) { - v = f32( ( -randu()*1e3 ) - MAXLOGF ); + v = f32( ( -randu()*1e3 ) - MAXLOGF - 1.0 ); y = coshf( v ); t.strictEqual( y, PINF, 'returns expected value' ); } t.end(); }); + +tape( 'the function does not overflow for values whose hyperbolic cosine is representable', opts, function test( t ) { + var expected; + var x; + var v; + var i; + + x = [ 89.0, 89.2, 89.4 ]; + expected = [ + 2.2448063889081578e+38, + 2.741804322770982e+38, + 3.3488627241070614e+38 + ]; + for ( i = 0; i < x.length; i++ ) { + v = coshf( f32( x[ i ] ) ); + t.strictEqual( isAlmostSameValue( v, expected[ i ], 2 ), true, 'returns expected value' ); + v = coshf( f32( -x[ i ] ) ); + t.strictEqual( isAlmostSameValue( v, expected[ i ], 2 ), true, 'returns expected value' ); + } + t.end(); +}); diff --git a/lib/node_modules/@stdlib/math/base/special/sinhf/lib/main.js b/lib/node_modules/@stdlib/math/base/special/sinhf/lib/main.js index 57307760e9bf..8e22ad1cc5ec 100644 --- a/lib/node_modules/@stdlib/math/base/special/sinhf/lib/main.js +++ b/lib/node_modules/@stdlib/math/base/special/sinhf/lib/main.js @@ -38,8 +38,6 @@ var isnanf = require( '@stdlib/math/base/assert/is-nanf' ); var isInfinitef = require( '@stdlib/math/base/assert/is-infinitef' ); var expf = require( '@stdlib/math/base/special/expf' ); var f32 = require( '@stdlib/number/float64/base/to-float32' ); -var PINF = require( '@stdlib/constants/float32/pinf' ); -var NINF = require( '@stdlib/constants/float32/ninf' ); var MAXLOGF = require( '@stdlib/constants/float32/max-ln' ); var polyvalP = require( './polyval_p.js' ); @@ -110,12 +108,14 @@ function sinhf( x ) { } else { a = -x; } - // Handle overflow: - if ( a > MAXLOGF ) { - if ( x > ZERO ) { - return PINF; + // Avoid premature overflow by evaluating `exp(|x|/2) * exp(|x|/2) / 2`: + if ( a >= MAXLOGF ) { + z = expf( f32( ONE_HALF * a ) ); + z = f32( f32( ONE_HALF * z ) * z ); + if ( x < ZERO ) { + z = -z; } - return NINF; + return z; } if ( a > ONE ) { z = expf( a ); diff --git a/lib/node_modules/@stdlib/math/base/special/sinhf/manifest.json b/lib/node_modules/@stdlib/math/base/special/sinhf/manifest.json index bcdbecefbf67..0dc7c57d2eab 100644 --- a/lib/node_modules/@stdlib/math/base/special/sinhf/manifest.json +++ b/lib/node_modules/@stdlib/math/base/special/sinhf/manifest.json @@ -40,8 +40,6 @@ "@stdlib/math/base/assert/is-nanf", "@stdlib/math/base/assert/is-infinitef", "@stdlib/math/base/special/expf", - "@stdlib/constants/float32/pinf", - "@stdlib/constants/float32/ninf", "@stdlib/constants/float32/max-ln" ] }, @@ -59,8 +57,6 @@ "@stdlib/math/base/assert/is-nanf", "@stdlib/math/base/assert/is-infinitef", "@stdlib/math/base/special/expf", - "@stdlib/constants/float32/pinf", - "@stdlib/constants/float32/ninf", "@stdlib/constants/float32/max-ln" ] }, @@ -78,8 +74,6 @@ "@stdlib/math/base/assert/is-nanf", "@stdlib/math/base/assert/is-infinitef", "@stdlib/math/base/special/expf", - "@stdlib/constants/float32/pinf", - "@stdlib/constants/float32/ninf", "@stdlib/constants/float32/max-ln" ] } diff --git a/lib/node_modules/@stdlib/math/base/special/sinhf/src/main.c b/lib/node_modules/@stdlib/math/base/special/sinhf/src/main.c index d842a99d159f..9ca621e0f3e3 100644 --- a/lib/node_modules/@stdlib/math/base/special/sinhf/src/main.c +++ b/lib/node_modules/@stdlib/math/base/special/sinhf/src/main.c @@ -33,8 +33,6 @@ #include "stdlib/math/base/special/sinhf.h" #include "stdlib/math/base/assert/is_nanf.h" #include "stdlib/math/base/assert/is_infinitef.h" -#include "stdlib/constants/float32/pinf.h" -#include "stdlib/constants/float32/ninf.h" #include "stdlib/constants/float32/max_ln.h" #include "stdlib/math/base/special/expf.h" @@ -106,12 +104,14 @@ float stdlib_base_sinhf( const float x ) { } else { a = -x; } - // Handle overflow: - if ( a > STDLIB_CONSTANT_FLOAT32_MAX_LN ) { - if ( x > 0.0f ) { - return STDLIB_CONSTANT_FLOAT32_PINF; + // Avoid premature overflow by evaluating `exp(|x|/2) * exp(|x|/2) / 2`: + if ( a >= STDLIB_CONSTANT_FLOAT32_MAX_LN ) { + z = stdlib_base_expf( 0.5f * a ); + z = ( 0.5f * z ) * z; + if ( x < 0.0f ) { + z = -z; } - return STDLIB_CONSTANT_FLOAT32_NINF; + return z; } if ( a > 1.0f ) { z = stdlib_base_expf( a ); diff --git a/lib/node_modules/@stdlib/math/base/special/sinhf/test/test.js b/lib/node_modules/@stdlib/math/base/special/sinhf/test/test.js index 76ccc9a9e014..5b1fa79481cd 100644 --- a/lib/node_modules/@stdlib/math/base/special/sinhf/test/test.js +++ b/lib/node_modules/@stdlib/math/base/special/sinhf/test/test.js @@ -130,14 +130,14 @@ tape( 'the function returns `-0` if provided `-0`', function test( t ) { t.end(); }); -tape( 'the function returns `+infinity` if provided a value greater than `MAXLOGF`', function test( t ) { - var v = sinhf( f32( 89.0 ) ); +tape( 'the function returns `+infinity` if provided a value greater than `MAXLOGF + ln(2)`', function test( t ) { + var v = sinhf( f32( 90.0 ) ); t.strictEqual( v, PINF, 'returns expected value' ); t.end(); }); -tape( 'the function returns `-infinity` if provided a value less than `-MAXLOGF`', function test( t ) { - var v = sinhf( f32( -89.0 ) ); +tape( 'the function returns `-infinity` if provided a value less than `-MAXLOGF - ln(2)`', function test( t ) { + var v = sinhf( f32( -90.0 ) ); t.strictEqual( v, NINF, 'returns expected value' ); t.end(); }); @@ -197,3 +197,24 @@ tape( 'the function handles subnormal inputs', function test( t ) { t.end(); }); + +tape( 'the function does not overflow for values whose hyperbolic sine is representable', function test( t ) { + var expected; + var x; + var v; + var i; + + x = [ 89.0, 89.2, 89.4 ]; + expected = [ + 2.2448063889081578e+38, + 2.741804322770982e+38, + 3.3488627241070614e+38 + ]; + for ( i = 0; i < x.length; i++ ) { + v = sinhf( f32( x[ i ] ) ); + t.strictEqual( isAlmostSameValuef( v, expected[ i ], 2 ), true, 'returns expected value' ); + v = sinhf( f32( -x[ i ] ) ); + t.strictEqual( isAlmostSameValuef( v, -expected[ i ], 2 ), true, 'returns expected value' ); + } + t.end(); +}); diff --git a/lib/node_modules/@stdlib/math/base/special/sinhf/test/test.native.js b/lib/node_modules/@stdlib/math/base/special/sinhf/test/test.native.js index 1cc78c369e05..b613540206c6 100644 --- a/lib/node_modules/@stdlib/math/base/special/sinhf/test/test.native.js +++ b/lib/node_modules/@stdlib/math/base/special/sinhf/test/test.native.js @@ -139,14 +139,14 @@ tape( 'the function returns `-0` if provided `-0`', opts, function test( t ) { t.end(); }); -tape( 'the function returns `+infinity` if provided a value greater than `MAXLOGF`', opts, function test( t ) { - var v = sinhf( f32( 89.0 ) ); +tape( 'the function returns `+infinity` if provided a value greater than `MAXLOGF + ln(2)`', opts, function test( t ) { + var v = sinhf( f32( 90.0 ) ); t.strictEqual( v, PINF, 'returns expected value' ); t.end(); }); -tape( 'the function returns `-infinity` if provided a value less than `-MAXLOGF`', opts, function test( t ) { - var v = sinhf( f32( -89.0 ) ); +tape( 'the function returns `-infinity` if provided a value less than `-MAXLOGF - ln(2)`', opts, function test( t ) { + var v = sinhf( f32( -90.0 ) ); t.strictEqual( v, NINF, 'returns expected value' ); t.end(); }); @@ -206,3 +206,24 @@ tape( 'the function handles subnormal inputs', opts, function test( t ) { t.end(); }); + +tape( 'the function does not overflow for values whose hyperbolic sine is representable', opts, function test( t ) { + var expected; + var x; + var v; + var i; + + x = [ 89.0, 89.2, 89.4 ]; + expected = [ + 2.2448063889081578e+38, + 2.741804322770982e+38, + 3.3488627241070614e+38 + ]; + for ( i = 0; i < x.length; i++ ) { + v = sinhf( f32( x[ i ] ) ); + t.strictEqual( isAlmostSameValuef( v, expected[ i ], 2 ), true, 'returns expected value' ); + v = sinhf( f32( -x[ i ] ) ); + t.strictEqual( isAlmostSameValuef( v, -expected[ i ], 2 ), true, 'returns expected value' ); + } + t.end(); +});