It started off a regular Wednesday morning when I hear from my desk a colleague
muttering about doubles and their hex representation. "But that doesn't look
right", "How do I read this as a float", and "~~redacted~~ you're the engineer,
you do it". My interest piqued, I headed over to his desk to enquire about the
great un-solvable mystery of the double and its hex representation. The number
which would consume me for the rest of the morning: 0xc00000001568fba0.

## That's a Perfectly Valid hex Number!

I hear you say. And you're right, if we were to treat this as a long it
would simply be 13835058055641365408 (or -4611686018068186208 if we assume
a signed value). But we happen to know that this particular piece of data which
we have printed is supposed to represent a double (-2 to be precise). "Well
print it as a double" I hear from the back, and once again we *should* all know
that this can be achieved rather easily by using the %f/%e/%g specifiers in our
print statement. The only problem is that in kernel land (where we use printk)
we are limited to printing fixed point numbers, hence why our only *easy*
option was to print our double in it's raw hex format.

This is the point where we all think back to that university course where number representations were covered in depth, and terms like 'mantissa' and 'exponent' surface in our minds. Of course as we rack our brains we realise there's no way that we're going to remember exactly how a double is represented and bring up the IEEE 754 Wikipedia page.

## What is a Double?

Taking a step back for a second, a double (or a double-precision floating-point) is a number format used to represent floating-point numbers (those with a decimal component). They are made up of a sign bit, an exponent and a fraction (or mantissa):

Where the number they represent is defined by:

So this means that a 1 in the MSB (sign bit) represents a negative number, and we have some decimal component (the fraction) which we multiply by some power of 2 (as determined by the exponent) to get our value.

## Alright, so what's 0xc00000001568fba0?

The reason we're all here to be begin with, so what's 0xc00000001568fba0 if we treat it as a double? We can first split it into the three components:

##### 0xc00000001568fba0:

Sign bit: 1 -> Negative

Exponent: 0x400 -> 2^{(1024 - 1023)}

Fraction: 0x1568fba0 -> 1.*something*

And then use the formula above to get our number:

(-1)^{1} x 1.* something* x 2

^{(1024 - 1023)}

**But there's a much easier way!** Just write ourselves a little program in
userspace (where we are capable of printing floats) and we can save ourselves
*most* of the trouble.

```
#include <stdio.h>
void main(void)
{
long val = 0xc00000001568fba0;
printf("val: %lf\n", *((double *) &val));
}
```

So all we're doing is taking our hex value and storing it in a long (val), then
getting a pointer to val, casting it to a double pointer, and dereferencing it
and printing it as a float. * Drum Roll* And the answer is?

"val: -2.000000"

"Wait a minute, that doesn't quite sound right". You're right, it does seem a bit strange that this is exactly -2. Well it may be that we are not printing enough decimal places to see the full result, so update our print statement to:

```
printf("val: %.64lf\n", *((double *) &val));
```

And now we get:

"val: -2.0000001595175973534423974342644214630126953125000000"

Much better... But still where did this number come from and why wasn't it the -2 that we were expecting?

## Kernel Pointers

At this point suspicions had been raised that what was being printed by my colleague was not what he expected and that this was in fact a Kernel pointer. How do you know? Lets take a step back for a second...

In the PowerPC architecture, the address space which can be seen by an
application is known as the *effective* address space. We can take this
and translate it into a *virtual* address which when mapped through the
HPT (hash page table) gives us a *real* address (or the hardware memory address).

The *effective* address space is divided into 5 regions:

As you may notice, Kernel addresses begin with 0xc. This has the advantage that
we can map a *virtual* address without the need for a table by simply
masking the top nibble.

Thus it would be reasonable to assume that our value (0xc00000001568fba0) was indeed a pointer to a Kernel address (and further code investigation confirmed this).

## But What is -2 as a Double in hex?

Well lets modify the above program and find out:

```
include <stdio.h>
void main(void)
{
double val = -2;
printf("val: 0x%lx\n", *((long *) &val));
}
```

Result?

"val: 0xc000000000000000"

Now that sounds much better. Lets take a closer look:

##### 0xc000000000000000:

Sign Bit: 1 -> Negative

Exponent: 0x400 -> 2^{(1024 - 1023)}

Fraction: 0x0 -> Zero

So if you remember from above, we have:

(-1)^{1} x 1.* 0* x 2

^{(1024 - 1023)}= -2

What about -1? -3?

#### -1:

##### 0xbff0000000000000:

Sign Bit: 1 -> Negative

Exponent: 0x3ff -> 2^{(1023 - 1023)}

Fraction: 0x0 -> Zero

(-1)^{1} x 1.* 0* x 2

^{(1023 - 1023)}= -1

#### -3:

##### 0xc008000000000000:

Sign Bit: 1 -> Negative

Exponent: 0x400 -> 2^{(1024 - 1023)}

Fraction: 0x8000000000000 -> 0.5

(-1)^{1} x 1.* 5* x 2

^{(1024 - 1023)}= -3

## So What Have We Learnt?

**Firstly**, make sure that what you're printing is what you think you're printing.

**Secondly**, if it looks like a Kernel pointer then you're probably not printing
what you think you're printing.

**Thirdly**, all Kernel pointers ~= -2 if you treat them as a double.

And **Finally**, *with my morning gone*, I can say for certain that if we treat it as
a double, 0xc00000001568fba0 =
-2.0000001595175973534423974342644214630126953125.