-
-
Notifications
You must be signed in to change notification settings - Fork 705
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a D implementation for std.math.ilogb #3186
Conversation
| @@ -2519,6 +2519,21 @@ unittest | |||
| } | |||
| } | |||
|
|
|||
| private import core.bitop : bsr; | |||
| static if (2*size_t.sizeof == ulong.sizeof) | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you really sure this is actually needed?
|
@Orvid The code does need a bsr that works for 64-bit numbers on all platforms (32 and 64 bit), which druntime does not provide. But you are right: it is not needed outside ilogb. To fix further function name resolution problems, I've renamed the 64-bit bsr to bsr_ulong. I've rebased the changes into one commit. |
| ret ; | ||
| size_t* uix = cast(size_t*)&x; | ||
| size_t msb = uix[MANTISSA_MSB]; | ||
| size_t lsb = uix[MANTISSA_LSB]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: I would prefer
uint msb = x >> 32;
if (msb)
return bsr(msb) + 32;
else
return bsr(cast(uint)x);
Over pointer casting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks.
That makes it CTFE-able I think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Safety0ff That also gets rid of Endianness-noise. I've amended the PR.
af51714
to
55a2a8d
Compare
|
The testbot failure is (as far as I can see) completely unrelated to this PR. |
| version (Win64_DMD_InlineAsm) | ||
| import core.bitop : bsr; | ||
| // Provide a bsr implementation for ulong in 32-bit mode | ||
| int bsr_ulong(ulong x) @trusted pure nothrow @nogc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you move this function outside from ilogb?
static import core.bitop.bsr;
...
static if(size_t.sizeof == 4)
{
int bsr_ulong(ulong x) @trusted pure nothrow @nogc
{
...
}
}
else
private alias bsr_ulong = core.bitop.bsr;The reason is to reduce template bloat.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's also a bsr64 implementation in std.numeric - it's private, however... https://github.com/D-Programming-Language/phobos/blob/965744f7d010082a8e2a68c3ec45734f5d845aff/std/numeric.d#L85
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... it is outdated. Looks like we need package bsr64 in std.math.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ibuclaw How sure are you that the bsr64 in std.numeric works for both little endian and big endian? Using a union instead of bitshifts makes a large assumption about memory layout that I am not sure is justified.
| static if (F.realFormat == RealFormat.ieeeExtended) | ||
| { | ||
| if (ex) | ||
| { // If exponent is non-zero |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do believe the coding style of Phobos discourages comments nestled in on the same line as opening braces. Please move this and others on the next line.
| ushort[T.sizeof/2] vu; | ||
| uint[T.sizeof/4] vui; | ||
| static if(T.sizeof >= 8) | ||
| long[T.sizeof/8] vl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, T.sizeof could be 12. So a more complicated logic other than dividing by 8 is required. What I may do in future is move this to be a part of floatTraits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the case of T.sizeof == 12 or 10, I was assuming this would truncate to long[1], which is fine? (the code knows about this)
edit: because real.sizeof==10, this case is tested already and seems to work :)
edit2: OK, I did not think this through thoroughly... there is the potential for problems with memory layout of union members smaller than the union size. The original code (parts from frexp) may have a similar problem with bigendian/littleendian with 80bit reals. However, in case of trouble, the unittests will fail.
|
rebased for cleaner history. |
|
removed trailing whitespace, and rebased |
|
Take a look at #3499 . |
|
No, only FYI. It can be merged with current implementation, thought. |
|
OK, sorry for my half-angry comment... :( |
|
@9il anything to review here or is it good to go? |
|
LGTM except unittest. foreach(F; TypeTuple!(float, double real))
{
...
} |
|
@9il I've tried to generalize the unittests, using a construct found elsewhere in math.d. (I am very new to D) Thanks for the patience. |
|
Thank you for math PRs :) |
| tuple( -F.infinity, int.max ), | ||
| tuple( 2.0 , 1 ), | ||
| tuple( 2.0001 , 1 ), | ||
| tuple( 1.9999 , 0 ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To fix auto test failure use construction like F(1.9999) instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
other option:
alias T = Tuple!(F, int);
T[] vals =
[
T(1.9999, 1),
....
];
|
@JohanEngelen You can use fixed size arrays to prevent GC allocations. |
…oating point types.
|
@9il @DmitryOlshansky All done now! |
|
Going to merge in day or so unless somebody rises an issue with it. |
|
Hm a potential regression with this is that it was callable wirg int by implicit conversion and now it won't match the constraint. |
|
@DmitryOlshansky How is this solved in general in Phobos? |
|
In this case I'd add an overload for isIntegral that internally just does cast(double) for 32bit nunbers and below and cast(real) for everything else. On the other hand - log2 is trivially implementable for integers with test and bsr. |
|
The overload should be mark with deprecated, thought. |
|
@9il I'm not so sure - it's quite customary to supply integer literal for FP function expecting implicit conversion. Otherwise we'd get welcome user with hard to understand 'doesn't match overload set'. |
|
@DmitryOlshansky This is bad practice that can cause out-of-the-way errors in mathematical algorithms. However, if a function can have correct and optimal implementation for integer types (like |
|
@9il I know back in the university I've seen plenty of bugs in numerics due to use of int in C programs. |
|
Do you want me to add a unittest explicitly testing for a few (large) integer numbers? |
Yes, integer version needs some tests to cover it. |
| assert(ilogb(nextUp(-float.min_normal)) == -127); | ||
| assert(ilogb(nextUp(-0.0F)) == -149); | ||
| static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) { | ||
| assert(ilogb(nextUp(-float(0.0))) == -149); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL
|
@9il @DmitryOlshansky I've added implementations and tests for integer ilogb arguments. What do you think? |
|
Lookin' good |
|
Auto-merge toggled on |
Add a D implementation for std.math.ilogb
The reasons for this PR:
Some quick benchmarking showed that it is indeed quite a bit faster, especially (not surprising) for float and double. But the first reason is my main motivation.
I do not know how to do the casts to enable CTFE.
I added a few extra unittests to increase code coverage.
(edit: removed wrong comment about ARM)