feat: Add C implementation math/base/special/log10f#10277
feat: Add C implementation math/base/special/log10f#10277InFiNiTy0639 wants to merge 2 commits intostdlib-js:developfrom
math/base/special/log10f#10277Conversation
|
@kgryte @Planeshifter Please Review this PR thanks |
lib/node_modules/@stdlib/math/base/special/log10f/benchmark/benchmark.js
Outdated
Show resolved
Hide resolved
lib/node_modules/@stdlib/math/base/special/log10f/benchmark/benchmark.js
Show resolved
Hide resolved
|
I noticed your PR description contains closing keywords ("Resolves", "Closes", or "Fixes") referencing a "Tracking Issue". Why this matters: Required action: Thank you for your contribution to the project! |
math/base/special/log10f
@stdlib-bot Why did you add a potential duplicate module?, IN Issue #649 |
Removed unused variable declaration for options in benchmark. Signed-off-by: MA RIZWAN <148482027+InFiNiTy0639@users.noreply.github.com>
That is probably because there is already an open PR for log10f: #8873 |
Yeah ,I also just checked this, but the PR is still not merged. I think I should work on other packages. 😢 |
math/base/special/log10fmath/base/special/log10f
Planeshifter
left a comment
There was a problem hiding this comment.
Thanks for opening a PR!
| * @param x input value | ||
| * @return output value | ||
| */ | ||
| float stdlib_base_log10f( const float x ) { |
There was a problem hiding this comment.
The parameter x is declared as const float, but it gets modified on line 70 (x *= TWO25) and line 83 (stdlib_base_float32_from_word(..., &x)). This will cause a compilation error.
I propose introducing a local float xc;, assign xc = x; near the top, and replace all subsequent uses of x with xc (see how lnf/src/main.c does it).
| function log10f(x) { | ||
| var hfsq; | ||
| var hi; | ||
| var lo; | ||
| var r; | ||
| var f; | ||
| var y; | ||
| var hx; | ||
| var i; | ||
| var k; | ||
|
|
||
| if (x === 0.0) { | ||
| return NINF; | ||
| } | ||
| if (isnanf(x) || x < 0.0) { | ||
| return NaN; | ||
| } | ||
| hx = toWord(x); | ||
| k = 0; | ||
| if (hx < FLT_MIN_NORMAL_EXP) { | ||
| // x < 2**-126 | ||
| if ((hx & 0x7fffffff) === 0) { | ||
| return NINF; // log(+-0) = -inf | ||
| } | ||
| k -= 25; | ||
| x *= TWO25; // subnormal number, scale up x | ||
| hx = toWord(x); | ||
| } | ||
| if (hx >= FLT_MAX_EXP_BIT_MASK) { | ||
| return x + x; | ||
| } | ||
| if (hx === FLT_BIAS_EXP) { | ||
| return 0.0; // log(1) = +0 | ||
| } | ||
| k += (hx >>> 23) - 127; | ||
| hx &= 0x007fffff; | ||
| i = (hx + 0x95f64) & 0x800000; | ||
| // Normalize x or x/2... | ||
| x = fromWord(hx | (i ^ FLT_BIAS_EXP)); | ||
| k += (i >>> 23); | ||
| y = k; | ||
| f = x - 1.0; | ||
| hfsq = 0.5 * f * f; | ||
| r = kernelLog1pf(f); | ||
|
|
||
| hi = f - hfsq; | ||
| hx = toWord(hi); | ||
| hi = fromWord(hx & 0xfffff000); | ||
| lo = (f - hi) - hfsq + r; | ||
| return (y * LOG10_2LO) + ((lo + hi) * IVLN10) + (y * LOG10_2HI); |
There was a problem hiding this comment.
This is the most critical issue in this PR: the implementation is missing float64ToFloat32 wrapping around all arithmetic operations. In JavaScript, all math is performed in float64 by default. For a single-precision function, every intermediate computation must be explicitly rounded to float32 via float64ToFloat32(), or the results will be computed in double precision.
Take a look at how lnf/lib/main.js wraps every arithmetic expression in float64ToFloat32(...). For example, your line:
f = x - 1.0;should be:
f = float64ToFloat32( x - 1.0 );And:
hfsq = 0.5 * f * f;should be:
hfsq = float64ToFloat32( 0.5 * float64ToFloat32( f * f ) );This needs to be applied to every arithmetic expression in this function. You'll also need to add the float64ToFloat32 import at the top.
| var toWord = require('@stdlib/number/float32/base/to-word'); | ||
| var fromWord = require('@stdlib/number/float32/base/from-word'); | ||
| var isnanf = require('@stdlib/math/base/assert/is-nanf'); | ||
| var kernelLog1pf = require('@stdlib/math/base/special/kernel-log1pf'); | ||
| var NINF = require('@stdlib/constants/float32/ninf'); |
There was a problem hiding this comment.
A few style issues here:
- stdlib uses spaces inside parentheses for
requirecalls:require( '...' )notrequire('...'). This applies everywhere in the file. - The file uses 4-space indentation, but stdlib requires TAB indentation for
.jsfiles (per.editorconfig). - You're missing a
float64ToFloat32import, which you'll need for the correctness fix mentioned below.
Please also check the lnf package for the expected formatting -- it's the closest reference implementation.
|
|
||
| @license Apache-2.0 | ||
|
|
||
| Copyright (c) 2018 The Stdlib Authors. |
There was a problem hiding this comment.
The copyright year should be 2025 for a new package, not 2018.
| Copyright (c) 2018 The Stdlib Authors. | |
| Copyright (c) 2025 The Stdlib Authors. |
|
|
||
| var tape = require('tape'); | ||
| var isnanf = require('@stdlib/math/base/assert/is-nanf'); | ||
| var isInfinitef = require('@stdlib/math/base/assert/is-infinitef'); |
There was a problem hiding this comment.
isInfinitef is imported but never used. Please remove the unused import.
Also, all require calls in this file need spaces inside parentheses (e.g., require( 'tape' ) not require('tape')), and indentation should use tabs, not spaces.
| tape('the function matches Math.log10 results within tolerance', function test(t) { | ||
| var expected; | ||
| var delta; | ||
| var tol; | ||
| var x; | ||
| var y; | ||
| var i; | ||
|
|
||
| for (i = 0; i < 100; i++) { | ||
| x = (Math.random() * 100.0) + 0.1; | ||
| x = float64ToFloat32(x); | ||
| y = log10f(x); | ||
| expected = float64ToFloat32(Math.log10(x)); | ||
| delta = absf(y - expected); | ||
| tol = 2.0 * EPS * absf(expected); | ||
| if (tol < EPS) { | ||
| tol = EPS; | ||
| } | ||
| t.ok(delta <= tol, 'within tolerance. x: ' + x + '. y: ' + y + '. expected: ' + expected + '. delta: ' + delta + '. tol: ' + tol); | ||
| } | ||
| t.end(); |
There was a problem hiding this comment.
Using Math.random() makes these tests non-deterministic -- they could pass on one run and fail on another. The stdlib convention is to use pre-generated test fixtures (typically from Julia or similar) stored as JSON files in test/fixtures/. Take a look at lnf/test/test.js for the expected pattern with fixture-based tests covering different value ranges (large positive, small positive, subnormal, etc.).
| #include <stdlib.h> | ||
| #include <stdio.h> | ||
|
|
||
| int main() { |
There was a problem hiding this comment.
stdlib convention uses int main( void ) instead of int main().
| int main() { | |
| int main( void ) { |
| ] | ||
| } | ||
| ] | ||
| } No newline at end of file |
There was a problem hiding this comment.
This file is missing a trailing newline at the end, which is required by the project conventions. Same issue in package.json.
| var bench = require('@stdlib/bench'); | ||
| var uniform = require('@stdlib/random/array/uniform'); | ||
| var isnanf = require('@stdlib/math/base/assert/is-nanf'); | ||
| var float64ToFloat32 = require('@stdlib/number/float64/base/to-float32'); |
There was a problem hiding this comment.
float64ToFloat32 is imported but never used in this file -- it can be removed. Also, the same spacing/indentation style issues apply here (tabs, spaces inside parens).
Progresses #649.
Description
This pull request:
lib/node_modules/@stdlib/math/base/special/log10fRelated Issues
This Pull request:
Questions
No.
Other
Open to reviews!!
Passed Tests Screenshots
Checklist
AI Assistance
If you answered "yes" above, how did you use AI assistance?
Disclosure
Research and understanding
@stdlib-js/reviewers