diff mbox series

manual: logb(x) is floor(log2(fabs(x)))

Message ID 20240304224051.4862-1-alx@kernel.org
State New
Headers show
Series manual: logb(x) is floor(log2(fabs(x))) | expand

Commit Message

Alejandro Colomar March 4, 2024, 10:40 p.m. UTC
log2(3) doesn't accept negative input, but it seems logb(3) does accept
it.

Link: <https://lore.kernel.org/linux-man/ZeYKUOKYS7G90SaV@debian/T/#u>
Reported-by: Morten Welinder <mwelinder@gmail.com>
Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
---
 manual/math.texi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Vincent Lefevre March 5, 2024, 10:02 a.m. UTC | #1
On 2024-03-04 23:40:59 +0100, Alejandro Colomar wrote:
> log2(3) doesn't accept negative input, but it seems logb(3) does
> accept it.

Yes, but unrelated to that, there was an issue with the text before.

> Link: <https://lore.kernel.org/linux-man/ZeYKUOKYS7G90SaV@debian/T/#u>
> Reported-by: Morten Welinder <mwelinder@gmail.com>
> Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
> Signed-off-by: Alejandro Colomar <alx@kernel.org>
> ---
>  manual/math.texi | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/manual/math.texi b/manual/math.texi
> index 2f6ee253b9..bf4027c4ee 100644
> --- a/manual/math.texi
> +++ b/manual/math.texi
> @@ -561,7 +561,7 @@ These functions return the base-2 logarithm of @var{x}.
>  @safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
>  These functions extract the exponent of @var{x} and return it as a
>  floating-point value.  If @code{FLT_RADIX} is two, @code{logb} is equal
> -to @code{floor (log2 (x))}, except it's probably faster.
> +to @code{floor (log2 (fabs ((x)))}, except it's probably faster.

No, it isn't necessarily equal. The code floor (log2 (fabs ((x)))
can give an incorrect result due to rounding if x is just before
a power of 2, in particular if x is large.

#include <stdio.h>
#include <float.h>
#include <math.h>

int main (void)
{
  double x = DBL_MAX;

  printf ("x = %a\n", x);
  printf ("%a\n", log2 (fabs (x)));
  printf ("%a\n", floor (log2 (fabs (x))));
  printf ("%a\n", logb (x));
  return 0;
}

outputs

x = 0x1.fffffffffffffp+1023
0x1p+10
0x1p+10
0x1.ff8p+9

on an x86_64 machine.
Alejandro Colomar March 5, 2024, 11:10 a.m. UTC | #2
Hi Vincent!

On Tue, Mar 05, 2024 at 11:02:51AM +0100, Vincent Lefevre wrote:
> On 2024-03-04 23:40:59 +0100, Alejandro Colomar wrote:
> > log2(3) doesn't accept negative input, but it seems logb(3) does
> > accept it.
> 
> Yes, but unrelated to that, there was an issue with the text before.
> 
> > Link: <https://lore.kernel.org/linux-man/ZeYKUOKYS7G90SaV@debian/T/#u>
> > Reported-by: Morten Welinder <mwelinder@gmail.com>
> > Cc: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
> > Signed-off-by: Alejandro Colomar <alx@kernel.org>
> > ---
> >  manual/math.texi | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/manual/math.texi b/manual/math.texi
> > index 2f6ee253b9..bf4027c4ee 100644
> > --- a/manual/math.texi
> > +++ b/manual/math.texi
> > @@ -561,7 +561,7 @@ These functions return the base-2 logarithm of @var{x}.
> >  @safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
> >  These functions extract the exponent of @var{x} and return it as a
> >  floating-point value.  If @code{FLT_RADIX} is two, @code{logb} is equal
> > -to @code{floor (log2 (x))}, except it's probably faster.
> > +to @code{floor (log2 (fabs ((x)))}, except it's probably faster.
> 
> No, it isn't necessarily equal. The code floor (log2 (fabs ((x)))
> can give an incorrect result due to rounding if x is just before
> a power of 2, in particular if x is large.

Hmmm, the bug is different, so how about a second patch for fixing that?

For that, I'd do s/equal/similar/ and s/faster/more accurate/.  Does it
sound good to you?

Have a lovely day!
Alex
Vincent Lefevre March 5, 2024, 11:37 a.m. UTC | #3
On 2024-03-05 12:10:23 +0100, Alejandro Colomar wrote:
> Hi Vincent!
> 
> On Tue, Mar 05, 2024 at 11:02:51AM +0100, Vincent Lefevre wrote:
> > On 2024-03-04 23:40:59 +0100, Alejandro Colomar wrote:
> > > diff --git a/manual/math.texi b/manual/math.texi
> > > index 2f6ee253b9..bf4027c4ee 100644
> > > --- a/manual/math.texi
> > > +++ b/manual/math.texi
> > > @@ -561,7 +561,7 @@ These functions return the base-2 logarithm of @var{x}.
> > >  @safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
> > >  These functions extract the exponent of @var{x} and return it as a
> > >  floating-point value.  If @code{FLT_RADIX} is two, @code{logb} is equal
> > > -to @code{floor (log2 (x))}, except it's probably faster.
> > > +to @code{floor (log2 (fabs ((x)))}, except it's probably faster.

BTW, note that there is a missing closing parenthesis, or actually,
an opening parenthesis that could be removed, i.e.

  @code{floor (log2 (fabs (x)))}

> > No, it isn't necessarily equal. The code floor (log2 (fabs ((x)))
> > can give an incorrect result due to rounding if x is just before
> > a power of 2, in particular if x is large.
> 
> Hmmm, the bug is different, so how about a second patch for fixing that?

Yes.

> For that, I'd do s/equal/similar/ and s/faster/more accurate/.  Does it
> sound good to you?

I think that "more accurate" is misleading because this is actually
much more than "more accurate": logb is guaranteed to give the exact
result (the result is a small integer, thus exactly representable),
while floor (log2 (fabs (x))) may give an incorrect integer (there is
a small inaccuracy up to log2, but this becomes a much more important
problem due to the fact that floor is mathematically a discontinuous
function).

I would suggest:

@code{logb} is similar to @code{floor (log2 (fabs (x)))}, except that
the latter code may give an incorrect integer.

If needed, one can add an explanation: "due to intermediate rounding";
or an example: ", e.g. on DBL_MAX".

Regards,
Alejandro Colomar March 5, 2024, 12:30 p.m. UTC | #4
Hi Vincent,

On Tue, Mar 05, 2024 at 12:37:38PM +0100, Vincent Lefevre wrote:
> > > > -to @code{floor (log2 (x))}, except it's probably faster.
> > > > +to @code{floor (log2 (fabs ((x)))}, except it's probably faster.
> 
> BTW, note that there is a missing closing parenthesis, or actually,
> an opening parenthesis that could be removed, i.e.
> 
>   @code{floor (log2 (fabs (x)))}

Oops!


> > For that, I'd do s/equal/similar/ and s/faster/more accurate/.  Does it
> > sound good to you?
> 
> I think that "more accurate" is misleading because this is actually
> much more than "more accurate": logb is guaranteed to give the exact
> result (the result is a small integer, thus exactly representable),
> while floor (log2 (fabs (x))) may give an incorrect integer (there is
> a small inaccuracy up to log2, but this becomes a much more important
> problem due to the fact that floor is mathematically a discontinuous
> function).
> 
> I would suggest:
> 
> @code{logb} is similar to @code{floor (log2 (fabs (x)))}, except that
> the latter code may give an incorrect integer.
> 
> If needed, one can add an explanation: "due to intermediate rounding";

Agree; I'll take it, with the "due to ..." too.

> or an example: ", e.g. on DBL_MAX".

I'll omit this.

Have a lovely day!
Alex
Morten Welinder March 5, 2024, 1:16 p.m. UTC | #5
> No, it isn't necessarily equal. The code floor (log2 (fabs ((x)))
> can give an incorrect result due to rounding if x is just before
> a power of 2, in particular if x is large.

Right you are.

However, the formula is meant as an explanation, not a C code
substitute, so I think a bit of rewording can fix it.  Something like

...works like "floor (log2 (fabs (x)))" if that formula is computed
without rounding of intermediate values.

Same story as fma really.

M.
Vincent Lefevre March 5, 2024, 3:01 p.m. UTC | #6
On 2024-03-05 08:16:09 -0500, Morten Welinder wrote:
> > No, it isn't necessarily equal. The code floor (log2 (fabs ((x)))
> > can give an incorrect result due to rounding if x is just before
> > a power of 2, in particular if x is large.
> 
> Right you are.
> 
> However, the formula is meant as an explanation, not a C code
> substitute, so I think a bit of rewording can fix it.  Something like
> 
> ...works like "floor (log2 (fabs (x)))" if that formula is computed
> without rounding of intermediate values.

I don't think that giving an explanation as code is a good idea.
Giving a math formula is OK, though. Also note that a math formula
could be given for any radix, not just 2: floor(log_b |x|). I think
that this is important since there are 3 conventions to define an
exponent. So this must be explicit in any radix.

BTW, "These functions extract the exponent from the internal
floating-point representation of x and return it as a floating-point
value." should not use the word "representation", because this has
nothing to do with the representation (think of numbers that are not
normalized). And "extract" is misleading too, because for numbers
that are not normalized, the exponent is not extracted.

Something like

  These functions return the exponent of the floating-point number x
  (in radix FLT_RADIX) as a floating-point value.

> Same story as fma really.

No, not really. For fma, this is a math formula followed by rounding.
diff mbox series

Patch

diff --git a/manual/math.texi b/manual/math.texi
index 2f6ee253b9..bf4027c4ee 100644
--- a/manual/math.texi
+++ b/manual/math.texi
@@ -561,7 +561,7 @@  These functions return the base-2 logarithm of @var{x}.
 @safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
 These functions extract the exponent of @var{x} and return it as a
 floating-point value.  If @code{FLT_RADIX} is two, @code{logb} is equal
-to @code{floor (log2 (x))}, except it's probably faster.
+to @code{floor (log2 (fabs ((x)))}, except it's probably faster.
 
 If @var{x} is de-normalized, @code{logb} returns the exponent @var{x}
 would have if it were normalized.  If @var{x} is infinity (positive or