Search code examples
phpc++floating-pointipv6

Converting IPv6 addresses to 64-bit double


IPv6 addresses are 128-bit unsigned integers under the hood, so their range of values is [0, 3.40 × 10^38]

A 128-bit register can store 2^128 (over 3.40 × 10^38) different values. The range of integer values that can be stored in 128 bits depends on the integer representation used. With the two most common representations, the range is 0 through 340,282,366,920,938,463,463,374,607,431,768,211,455 (2^128 − 1) for representation as an (unsigned) binary number, https://en.wikipedia.org/wiki/128-bit

A double-precision float variable in whatever language, though, can be as high as ±1.79769313486231570e+308 , so it definitely can assume all the values a 128-bit int can, and even more.

I'm sure there is a ready-to-use function/library/piece of code to convert from IPv6 => double. Can anyone post code to do that, or a link?

Thanks

Preferably PHP, but you can post code in other languages and i will translate to PHP.

P.S. i've read this related question Can double be used to store and safely retrieve 128 bit IPv6? but IMO the answers were wrong, because you don't need a variable with the same bits as the IPv6 address (128-bit). You just need it to be able to assume all the possible values an IPv6 can have. As i said above, a double can represent al the possible values of an 128-bit int and even more, despite it being just 64-bit.

How's that possible?

This is the magic of floating-point registers. They are more CPU-intensive to do calculations with compared to integer ones, but are more powerful.

EDIT: Why do i need this conversion: i have a MySQL table with IPv6 addresses defined as DECIMAL(39), which is a 39-digit int. I need to query that table by visitor's address if it is IPv6.


Solution

  • A double-precision float variable in whatever language, though, can be as high as ±1.79769313486231570e+308 , so it definitely can assume all the values a 128-bit int can, and even more.

    This assumption is wrong. A double-precision float variable can not assume all the values a 128-bit int can.

    This is because the floating point type in computer science has limited precision. Once you get to numbers greater than 2^52, you can no longer represent individual integers in that range. You're limited to 2^52, 2^52+2, 2^52+4, 2^52+6, ... until you reach 2^53, at which point you lose more precision, only representing values 2^53, 2^53+4, 2^53+8, 2^53+12, ...

    So if, say, your IPv6 address is 2^52+1, a double would be incapable of representing it.

    So no, it is not possible to represent the whole range of IPv6 addresses in the space defined by a double, 64-bit floating point value.

    See Is Floating Point Math Broken? for a more detailed breakdown on how Floating Point math works in computers.


    Part of your issue appears to be some confusion between types. A DECIMAL type in computer science is not the same kind of type as a double type. decimal types are usually some kind of BigNum implementation. They probably can store an entire IPv6 address, but it's not going to do it in only 64-bits. It'll use whatever quantity of bits is required by the database implementation (probably more than 128 bits, since decimal is usually designed to handle fixed-point math).

    If you need to save an IPv6 address in a database, you might be able to get away with it with a decimal(39) type, but to be safe, you should be using one of these instead:

    • An explicitly sized 128-bit integer field
    • A 16-byte VARCHAR (or some other string-like field) that saves 8 bits per character
    • A 64-byte VARCHAR field that saves the IP Address in a Hexadecimal-encoded string (like how they are commonly represented).
    • A 22-byte VARCHAR field that saves the IP Address in a BASE64-encoded string