Bitboards using 2 DOUBLE's??

Discussion of chess software programming and technical issues.

Moderator: Ras

bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: Bitboards using 2 DOUBLE's??

Post by bob »

Carey wrote:Big snipping of dead wood to get this thing down to size....
bob wrote:
Carey wrote: Doesn't matter what I *want*.

The reality is that's what the FPU does. So you live with it.

You work with abstractions instead.
Let's get back to the _real_ world. The chess board can not be abstracted, because it has to be evaluated, and I have to explicitly prepare patterns to operate on the board representation. If the bits are going to get moved, then I have no idea how to prepare any patterns for anything from generating moves to evaluating positions...
Simple Bob, the FPU will move them around as needed. You don't have to worry about them.

Instead of worrying that A1 is in bit position 0, you instead know that A1 has a *value* of 2^0 and B1 has a value of 2^1 and so on. (Or however you want to rotate the bitboard and whether you want A1, H1 A8 or H8 to start at 2^0....)

It *exactly* like doing a fpu addition.
_except_ for your magic bit 53. To turn it on, you have to have a specific range of exponents. To turn it off, you have to use a specific exponent that indicates that the fraction is not normalized. I've thought about it a bit and do not see any sort of addition or multiplication that will correctly do this. Doesn't mean it can't work, but a shallow analysis suggests a problem.

And it really is all about nothing because no rational person would consider trying to use the "hidden 1 bit" for data, when you still need another 11 bits to build the entire 64 bit value anyway. So why jump through hoops to save one bit in one float and then only use 11 bits of the other float. I can easily see ways to use 32 bits in each of two doubles. It would be a kludge, but it could work. But trying to get to the hidden 1 bit looks to be quite difficult, if not impossible, and also certainly not worth the effort.


If you are adding 1 + 234235437 then nothing is going to be aligned while in the FPU register or stored in memory.

The '1' is going to be in that hidden 53rd bit and the other number is going to be spread out over a couple dozen upper bits.

But, as soon as you do FADD, the fpu is automagically going to align those decimal points and do the math right.

With the exception of the binary AND & OR (which are a problem), all the rest will work out as plain ordinary FPU math operations.

The FPU doesn't know it's really representing a chess bitmap. Since we are treating it like a number, it 's happy and will give us the right answer.


If I have squares A1 A3 and D5 set, then the FPU is going to put them wherever it wants. I have no real control over that (without jumping through lots of hoops and kludges etc. and I don't want to do that.)
Then this is a pointless discussion. Because I have to know where A1 is all the time. It can't "dance around" because I now need thousands of patterns to cover all the places where it might be, which is useless...

My original observations dealt with real-world realizable algorithms that might work. we have now gone far beyond that to something that appears to have no usefullness no matter how far out into the twilight one might want to venture.

The entire concept of bitboards is that each square on the board is represented by a specific bit in the bitmap. If that is not valid, then nothing I have said previously applies, as the idea is not just bad, it is unworkable.
We are still working with essentially bitboards. We're just letting the FPU think it's really a number.

You insisnt on treating them as a SET. A classic Pascal style SET. Which technically they are.

I'm treating them like they were numbers. Something the FPU is pretty good at doing (with the exception of AND & general purpose ORs.)

You shift by 8, I multiply by 256.

There really isn't that much difference, Bob.

Honestly.

There's very little difference in the two (with the exception of certain areas) except which ALU operates on the bitboard.

In yours, its the integer ALU. In my idea, it's the FPU's ALU.


Sure, it'll normalize things and move the bits up or down in the FPU word. But, it keeps track of it. *It* knows where the bits are really located even if I don't.

So, if later want to clear A1, then I load from a var that has A1 set. That var will also be normaled. (In fact, since it's a single bit, it'll actually be in that hidden 53rd bit.)

BUT, when I do Board-Sqr the FPU will automatically align things so that when it removes the A1 square from the board, it'll effect the right square.

That's just basic floating point math.
Here's the problem. I have a 52 or 53 bit value. They are accessed differently because of the exponent requirement. Let's suppose that we use exactly two exponents. The first is the smallest possible value that still leaves the fraction normalized, which means the hidden 1 bit is present. The second exponent is one less and indicates that now this is not a normalized number, which means the hidden bit is not present (or is zero).

What constant can I use as a multiplicand or addend, so that I can test bit 51 regardless of the exponent? The constant can't be the same for both cases, because of exponent alignment that has to match for add/subtract. This is the problem that I see being difficult to impossible. And at the best, unwarranted.

As long as you don't try to peek at the float's bits, it just doesn't matter because the math works.
Doesn't sound like it will work in a way that is _usable_ however. Because worrying about that 53 bit that you ought not be using in the first place, is going to cause _other_ bits to move around. So I now have to have patterns for cases where that barely usable bit is set and other patterns for where it is not set. That's beyond a kludge... The bits are all independent, and they can't move around and be usable by a real human programmer.]
Hmmm.... Maybe I see part of your problem....

You are thinking of bitboard patterns. And what do you do with bitboard patterns? You end up AND'ing them against each other.

In many cases, where you know you are setting or clearing a bit that is already clear or set, you can do it with addition & subtraction.

Try it with LongLong. It'll work. Stuff it into an Intel Long Double and it'll work. Stick it into some software based decimal math package and do it and it'll still work.

General purpose OR's are a problem though. Fortunately they are rare.

With the AND, though, you are definetly going to have a problem. The FPU just doesn't do that. (Unless somebody pops up with a MAGIC mul way to do even small ANDs)

For general AND's, we will have to align things and do them with the CPU's integer hardware or via table lookups because I don't see any reasonable way to do them in the FPU.

But I've said that since probably the first or second message, so that's not some new admission on my part.


Now when you do AND's, that 53rd bit can be a problem because the FPU doesn't know how to do an AND and you have to do it yourself.

BUT, if you are setting or clearing bits that you already know the state of (like in moving a piece), then it's not a problem. Addition and subtration will work properly and the FPU will deal with that hidden 53rd bit just like its designed to.

That 53rd bit can only be a problem when you try do a general purpose AND, like if you are masking an evaluation pattern.

And the only reason that becomes is a problem is you are no longer trying to do things in a math oriented way. You are trying to shift back and do things in classic SET ways.

Is there a math oriented way to do AND that will make the FPU happy? Dunno. I don't know of any, but I can't rule it out.

The concept of bitboards is that a specific bit represents a specific square. Going one level up as you are describing appears to me to be an impossible task, mentally...
It's not really. In fact, I bet you've been doing it for years.

Think of C's regular Long Long and doing operations on it.

If you do LL |= 1 or LL <<= 8 do you *really* know how the bits are arranged?
I _absolutely_ do know this. If I didn't I could not generate moves, evaluate positions, move pieces around, clear occupied squares, etc... I also know which bit it bit 0 and which bit is bit 63, and how they are distributed in between, and how each bit corresponds to a single square, etc... I care that the LSB is bit number 0, and I equate that to square A1. I care that the MSB is bit 63, and that square is H8. And I demand that the same bit be in the same place for all time. If the CPU wants to multiplex bits internally, I do not care, but I do care that the bits are numbered 0 thru 63, right to left, and that their positions _never_ change. This doesn't seem to fit the FP model you are describing, because I am going to have to pay attention to the exponent field to see if the bits have been shifted around to unusual locations...
Gotcha.

No, Bob, you don't know how the CPU stores the bits.

Take a simple, generic C based chess program around to nearly every system and it's going to work right.

Why? Because the C language requires that it work right. And the CPU takes steps to make sure things work right, no matter how it chooses to do things internally. If a CPU can't do it right, C compilers have to fake it in software. (Think 'long long' on an 16 or 32 bit system.)

It may be a little, big or even middle endian system.

Even the CPU registers themselves may store the bytes of a chess board in some out of order way.

All you know is that the C language organizes things as a series of 64 bits. You assume that 0x000000001 really is the lowest bit in the cpu register. (As safe assumption, but still technially an asumption.)

But you don't know what really goes on the CPU. For all you know, the CPU *could* be storing those integer values as FPU data internally and only converting to pure integer when storing them.

Or it could even be a decimal based computer. C would still work right and it would generate appropriate (complicated) code to do those 'simple' ANDs.

You don't know because the CPU and compiler hides all that from you.

You just said you don't care how the CPU handles them internally. Fine.

As long as the C oriented operations you do give you the right answer, you don't care.

So why are you so upset about the idea of it being in a FPU register? With the exception of AND, it'll still give you the same answer provided you concentrate on what the FPU can do well.

Meaning only clear bits that are already set or set bits that are already clear. Meaning addition and subtration.

And so on.

The only part you can't do reasonably easily are ANDs.


C guarantees the operations will work the same regardless of how it chooses to arange things in the CPU.
Not quite. It guarantees that x << 1 multiplies X by 2. If the bits are not adjacent, and the rightmost bit is the LSB, this will not work. So the view of the integer registers that I am presented is one that is consistent and which does not change based on the contents of the value. The sign bit will always be the MSB. Signed numbers are always stored in 2's complement with the high-order bits always set to 1, etc...
Really...

What about doing it in Decimal? You telling me that x<<1 still won't give x*2?

Of course it will.

Like you said, the C language requires (and common convention does anyway) that you be presented with a consistant interface & result.

That means what it wants to do internally is entirely up to it. As long as things look to you like you expect.

FPU does that, with the exception of AND, general purpose OR, provided you start treating the bitboard as a NUMBER rather than a SET.


You could be working on a 64 bit big endian system. Or a 64 bit little endian system. Or a 32 bit little endian system simulating 64 bit integers. Or a 32 bit big endian system simulating 64 bit integers. Or even an old 8 bit micro that is simulating 64 bit integers.
Endian is a memory issue, not an internal data representation issue. The bits are always numbered from LSB to MSB, right to left, when you work on them. I don't care how they are stored in memory. I very much care how they are ordered when in a register where I can get at 'em and do something with them.
Endian is a *storage* issue, not a memory issue.

You have endianness even in the CPU registers. Especially when you combine registers to make a longer one. But it knows how to make things work right.

The FPU is very similar. It stores the data in a way it likes. But it knows how to operate on it properly.





C even allows the bits to be out of order in the CPU registers!! As long as its consistant and does the operations correctly, C doesn't care.
Now we are out of the real world. I know where the LSB of a word is. C knows otherwise shift and multiply will not work correctly.
C would generate correct code to fake it.

(I'm not saying it does that. Just that if you were on a system where it had to do so, it would and program will still work correctly.
And there is the problem of BSF/BSR as well, which also correlate specific bit to a specific number.. C has the nasty habit of presenting binary data to me exactly as it is presented by the CPU. The ISA of the machine specifies all this in clear detail and C complies perfectly, at least for the cases I have used.
I didn't say your system didn't behave like you think.

I said C allows quite a few odd things. And yes, that includes bits in the middle of words that don't effect many things.

That's straight from one of the C language designers themselves. I used to read a lot of P. J. Plauger's articles in CUJ and Embedded Systems Programming and in several of them he talked about some of the odd things that C has to tolerate. Some of the decisions they made, and why. And some of the odd architectures that existed & were being built that they wanted to allow C to run on.

C was deliberately specified in a way that it would work right even on odd architectures. If the cpu architecture itself couldn't handle something the way C wanted, then the compiler writer was obligated to fake it in software.

C is very flexible in many ways, but it does have certain requirements and expectations.
So the "C doesn't care" is a hyperbolic statement, because C does specify how its data types are represented and how the various operators modify those values. C certainly doesn't care how the _cpu pipelines_ do their thing, but it definitely cares about the bit order. otherwise signed/unsigned could not work at any level.
C doesn't care how a lot of the hardware operates. If the hardware can't do what C wants, C has to fake it. On most systems today, C rarely has to do much faking.

That's not just something I popped out of my mouth. That's from the C89 language designers themselves.

If you were running on a pure decimal system that provided enough decimal digits to hold 2^64, then your program would still work. (Wtih a couple issues like overflow, but that would cause problems even if you worked on a real 128 bit cpu since you probably 'carelessly' depend on exactly 64 bits.)