Comparing large numbers in php and sql - php

I need to compare a very large number in php (30 digits long) with 2 numbers in my database. Whats a good way to do this? I tried using floats but its not precise enough and I don't know of a good way to use large numbers in php.

Have you tried using string comparison? Just make sure every number is padded with zeroes.
mysql> select "123123123123123123456456456"<"123123123123123123456456457";
+-------------------------------------------------------------+
| "123123123123123123456456456"<"123123123123123123456456457" |
+-------------------------------------------------------------+
| 1 |
+-------------------------------------------------------------+
Justed test this up to 200+ chars, works like a charm.

Check bcdcomp function

You could compare strings instead.
Depending on how you're fetching the data from the database, you may want to explicitly cast the integer to a string type in the SQL statement.
Other than that, there are several libraries in PHP that handle large integers, like BCMath and GMP.

Handling large numbers in PHP is done through either of two libraries: GMP or BC Math.
I haven't done this myself, so it may not be correct, but I think you'd have to take the string result from GMP or BC Math, and feed that into the query. Make sure you store your numbers as bigint.
Interestin fact: You might think BigInt would be limited to about 20 digits, and you'd be right, except for the fact that it has Mysql Magic:
You can always store an exact integer value in a BIGINT column by storing it using a string. In this case, MySQL performs a string-to-number conversion that involves no intermediate double-precision representation.

If they're -very- big, I'd compare them as strings even. First, if one is longer than the other, it wins. If they're the same length, compare digit by digit left-to-right - if two digits differ, the number with the bigger digit wins. This of course for Positive integers.

Related

Storing Age numbers expressed in decimals in MySQL

I have an age column that is currently varchar because i cannot figure out how to define it correctly.
the ages look like the following samples:
.03 / .3 / 1.33 / 20
i tried Decimal (4,2) but then i get something like 82.00! which i dont want.
I want under 1 to look like 0.2 or .2, or .25
I want age 82 to be just 82 and age 5 to be just 5.
do I have to do this in php after the case or is there a way to store the numbers without the extra 0's.
storing as varchar displays them correctly but does not allow for the proper search.
How you store a number and how you format it when printing it are not mutually exclusive things. Storing numbers as strings is about the worst thing you can do. Decimal(4,2) tells MySQL to store the number with up to 2 digits of precision for the integral part of your number and up to 2 digits of precision for the fractional part. This says nothing about how those digits can be printed, however. For example printf("%d", 1.01) gives you 1. Whereas printf("%.2f", 1.0123) gives you 1.01.
PHP has several functions that can assist with formatting such as sprintf, and number_format, not to mention Intl's NumberFormatter for i18n.
If all you want is to truncate the right-most 0s from the number when printing it you can try something like rtrim($number, '0.').
echo rtrim(1.00, '0.'); // 1
echo rtrim(1.10, '0.'); // 1.1
echo rtrim(1.11, '0.'); // 1.11
echo rtrim(10.01, '0.'); // 10.01
The only edge case you have to watch out for here is if the number is 0 you will end up with an empty string.
Storing age as a varchar will lead to unexpected behaviour for instance when you ORDER BY age in a query.
Use the right types that map to your data.
ON displaying numbers with/without decimal places, use your application language (PHP) to perform this function.

Encoding/Compressing a large integer into alphanumeric value

I have a very large integer 12-14 digits long and I want to encrypt/compress this to an alphanumeric value so that the integer can be recovered later from the alphanumeric value. I tried to convert this integer using a 62 base and tried to map those values to a-zA-Z0-9, but the value generated from this is 7 characters long. This length is still long enough and I want to convert to about 4-5 characters.
Is there a general way to do this or some method in which this can be done so that recovering the integer would still be possible? I am asking the mathematical aspects here but I would be programming this in PHP and I recently started programming in php.
Edit:
I was thinking in terms of assigning a masking bit and using this in a fashion to generate less number of Chars. I am aware of the fact that the range is not enough and that is the reason I was focusing on using a mathematical trick or a way of representation. The 62 base was an Idea that I already applied but is not working out.
14 digit decimal numbers can express 100,000,000,000,000 values (1014).
5 characters of a 62 character alphabet can express 916,132,832 values (625).
You cannot cram the equivalent number of values of a 14 digit number into a 5 character base 62 string. It's simply not possible to express each possible value uniquely. See http://en.wikipedia.org/wiki/Pigeonhole_principle. Even base 64 with 7 characters is not enough (only 4,398,046,511,104 possible values). In fact, if you target a 5 character short string you'd need to compensate by using a base 631 alphabet (6315 = 100,033,806,792,151).
Even compression doesn't help you. It would mean that two or more numbers would need to compress to the same compressed string (because there aren't enough possible unique compressed values), which logically means it's impossible to uncompress them into two different values.
To illustrate this very simply: Say my alphabet and target "string length" consists of one bit. That one bit can be 0 or 1. It can express 2 unique possible values. Say I have a compression algorithm which compresses anything and everything into this one bit. ... How could I possibly uncompress 100,000,000,000,000 unique values out of that one bit with two possible values? If you'd solve that problem, bandwidth and storage concerns would immediately evaporate and you'd be a billionaire.
With 95 printable ASCII characters you can switch to base 95 encoding instead of 62:
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
That way an integer string of length X can be compressed into length Y base 95 string, where
Y = X * log 10/ log 95 = roughly X / 2
which is pretty good compression. So from length 12 you get down to 6. If the purpose of compression is to save the bandwidth by using JSON, then base 92 can be good choice (excluding ",\,/ that become escaped in JSON).
Surely you can get better compression but the price to pay is a larger alphabet. Just replace 95 in the above formula by the number of symbols.
Unless of course, you know the structure of your integers. For instance, if they have plenty of zeroes, you can base your compression on this knowledge to get much better results.
because the pigeon principle you will end up with some values that get compressed and other values that get expanded. It simply impossible to create a compression algorithm that compress every possible input string (i.e. in your case your numbers).
If you force the cardinality of the output set to be smaller than the cardinality of the input set you'll get collisions (i.e. more input strings get "compressed" to the same compressed binary string). A compression algorithm should be reversible, right? :)

mysqli_fetch_assoc (& PDO fetch assoc) storing numbers as strings

as usual, did my duty to look everywhere for the sol but to no avail.
mysqli_fetch_assoc is (apparently) storing my numbers as strings.
Normally, I could care less, but my site is nearly 100% ajax, and it moves around a lot of data, so all of those json "'s start to add up.
If I'm just grabbing one column value, I can intval, but I want to grab whole rows with associated column names.
Is there a way to get mysqli_fetch_assoc (and PHP PDO's fetch assoc) to store numbers as numbers?
Many thanks in advance!
MySQL can store 64-bit numbers and unsigned numbers, neither of which are supported by default in PHP's int type. If you try to fetch numeric data from the database that overflows the int variables in PHP, you'll lose information. That's why PHP database clients fetch numbers as strings.
Re your comment:
Yes, if you're concerned about the size of JSON, convert the data in your PHP app for more compact representation.
Though since you said you're transferring column names as part of your JSON, it seems like the difference between string-formatted ints and binary ints is not your bottleneck anyway.
I mean, converting a string-int to a binary int reduces the bits by about 50%. But if you have column names as strings anyway, you're only shrinking a small portion of the JSON. The columns are still strings.

Create a unique 4-byte Integer number from a String in PHP

I have a SQL table which uses strings for a key. I need to convert that string (max. 18 Characters) to a unique (!) 4-byte integer using PHP. Can anyone help?
Unique? Not possible, sorry.
Let's take a closer look:
With 18 characters, even if we were assuming only the 128 possible characters of ASCII (7 bits), you'd get 128^18 possible strings (and I'm not even going into the possibility of shorter strings!), which is about 8E37 ( 8 and 37 zeroes ).
With a 4-byte integer, you're getting 256^4 possible integers, which is about 4E9 ( 4 billion ).
So, you have about 4E28 more strings than you have integers; you can't have an unique mapping.
Therefore, you'll definitely run into a collision as soon as you enter the 4294967297th key, but it is possible to run into one as soon as you enter more than one.
See also: http://en.wikipedia.org/wiki/Pigeonhole_principle
Keep a lookup-table of strings to integers. Everytime you encounter a new string you add it to the mapping table and assign it a new unique ID. This will work for about 2^32 strings which is probably enough.
There is no way to do this for more that 2^32 distinct strings.
You can't. A four-byte integer can represent 2^32 = 4 billion values, which is not enough to hold your target space.
If you currently have less then 4 billion rows in the table, you could create a cross table that just assigns an incremental value to each. You'd be limited to 4 billion rows with this approach, but this may be fine for your situation.

MySQL greater than with microtime timestamp

I have one PHP script inserting rows in a MySQL database. Each row has a field 'created_at' which is filled with the value of the PHP function microtime(true), and inserted as a double. (microtime because I need something more precise than to the second)
I have another PHP script that selects rows based on that created_at field.
When I go ahead and select like this:
SELECT * FROM `ms_voltage` WHERE created_at > 1302775523.51878
I receive a resultset with, as the first row, the row with exactly that value for created_at.
This occurs from within my PHP script and from within PhpMyAdmin when manually doing the query. But not always, not for every value. Just once and a while really.
How is this possible? I didn't ask for greater than/equals, I want strictly greater than.
Am I overlooking something type-related perhaps?
Yeah, floating point arithmetic can do that sometimes. To understand why, it's helpful to realize that just as not all numbers can be accurately represented in base 10, not all numbers can be accurately represented in base 2 either.
For example, "1/3" may be written in base 10 as 0.33333 or 0.33334. Neither is really "correct"; they're just the best we can do. A "DOUBLE" in base 10 might be 0.3333333333 or 0.3333333334, which is double the digits, yet still not "correct".
The best options are to either use a DECIMAL value, or use an INT value (and multiply your actual values by, say, 10000 or 100000 in order to get the decimal digits you care about into that int).
The DOUBLE type represent only approximate numeric data values. Try to use the DECIMAL type.
Is your column floating point? Calling microtime with true gives you a float, and that looks like a float, which will have digits after the .51878 that you don't see, so those digits make the stored value greater than the value you have in your query.
Unless you really need the float I'd convert the string result to an int, or even two columns for seconds and useconds. Then you can use > or < on known values without worrying about the imprecision of the floating point value.

Categories