I know the formular for conversion from Degree to Milliseconds and vice-versa. It can be implemented like that:
protected function decimal_to_milisecond($dec) {
if (!empty($dec)) {
$vars = explode(".",$dec);
if (count($vars) == 2) {
$deg = $vars[0];
$tempma = "0.".$vars[1];
$tempma = $tempma * 3600;
$min = floor($tempma / 60);
$sec = $tempma - ($min*60);
return round((((($deg * 60) + $min) * 60 + $sec) * 1000));
}
else return false;
} else return false;
}
function milisecond_to_decimal($sec) {
if (!empty($sec)) {
$s = $sec / 1000;
$d = (int)($s / 3600);
$s = $s % 3600;
$m = (int)($s / 60);
$s %= 60;
$ret = substr($d+((($m*60)+($s))/3600),0);
} else return null;
return $ret;
}
Scenario: I convert from Degree to Miliseconds and continue converting from Miliseconds to Degree. The converted value has some difference with original value. I want the value is exact as the orginal value as well. For example:
$lat = "1284146";
$long = "503136198";
$lat1 = milisecond_to_decimal($lat);
$long1 = milisecond_to_decimal($long);
$result1 = decimal_to_milisecond($lat1);
$result2 = decimal_to_milisecond($long1);
var_dump($result1, $result2);
The output is float(1284000) and float(503136000)
Is there another way to reduce difference is caused by conversion between degree and milliseconds?
There are 360 degrees (longitude), 60 minutes per degree, 60 secondes per minute, 1000 milliseconds per second. So at most
360*60*60*1000 milliseconds = 1 296 000 000 milliseconds
That fits well on 31 bits, so the idea would be to first convert to an integer, and perform as much operations as possible in integer.
Note that if you use single precision floating point, you'll get a 24 bits significand and will loose accuracy under 1 tenth of second (log2(360*60*60*10) is about 23.6).
I would recommend to store results in double precision (53 bits significand).
EDIT
What I was suggesting is to perform the conversion all at once, if there is a way to use double precision for representing $decimaldegrees (I don't know php enough to tell so), something like:
$millis = (int)( round( $decimaldegrees * (60*60*1000) ) );
Then if ever you want to decompose into DMS (but these variables are not used in your code):
$ms = $millis % 1000;
$sec = ($millis / 1000) % 60;
$min = ($millis / (60*1000)) % 60;
$deg = ($millis / (60*60*1000)) % 360;
Ater a deeper look at you code, it seems you are separating the decimal part first $tempma = "0.".$vars[1];
That could work if you work on the decimal representation string, because in this case that fits well even on a single precision float (log2(60*60*1000) is about 21.8). So the beginning could be replaced with:
$deg = (int) $vars[0];
$frac = "0.".$vars[1];
$millis = (int)( round( $frac * (60*60*1000) ) );
$millis = deg + millis;
From the example of output you gave, it sounds like the problem comes from the other conversion milisecond_to_decimal, presumably because some arithmetic operation is performed with integer arithmetic and thus discards the milliseconds.
Once again, I don't know php enough, but wouldn't $s = $s % 3600; in fact operate on (int)(%s) and thus discard the milliseconds?
You would need to find something equivalent to C function fmod or modf.
Once again, you could do all operation at once if there is a way to do it in double precision:
$decimaldegrees = ((double)($millis)) / (60*60*1000);
If you don't have access to double precision, you can't recompose safely, single precision does not have enough bits...
You would need to operate on separate string parts, caring of leading zeros in fraction part
Anyway, I strongly suggest to perform unit tests, that is to test your two functions separately, this way you'll get a better understanding of what works and what not.
Related
I want to use php to randomly generate 'nice numbers' for percentage problems aimed at children. Number 1 is the percentage and Number 2 is the total number. I have been attempting to do this but the execution is longer than 60 seconds. Is it possible to do the calculation and generation of nice numbers (where it finds / checks if is in an integer)?
This is for a plesk server, running MYSQL and PHP. I've tried checking the number of combinations of visual basic (there are 622) and it takes 0.4632853 seconds to complete.
function genRandomNumber(){
return (rand(5, 200));
}
function genRandomPercent(){
return (rand(5, 75));
}
$number1 = genRandomPercent();
$number2 = genRandomNumber();
if (is_int(($number1/100)*$number2)==false){
while (is_int(($number1/100)*$number2)==false) {
$number1 = genRandomPercent();
$number2 = genRandomNumber();
}
}
I expect the final output to have number1 and number2 where number1% of number2 is an integer. For example, number1 = 10 and number2 = 60, so 10% of 60 which is 6. Instead, I get an error message of 'PHP Fatal error: Maximum execution time of 60 seconds exceeded'
It is easier to test all combinations with 2 loops. To compare float values must work with eps.
<?php
$percentStart = 5;
$percentEnd = 75;
$numberStart = 5;
$numberEnd = 200;
$eps = 1.E-9;
$results=[];
for($percent = $percentStart; $percent <= $percentEnd; $percent++){
for($number = $numberStart; $number <= $numberEnd; $number++){
$percentFromNumber = $percent * $number/100;
if(fmod($percentFromNumber,1) < $eps){
$results[] = [
'percent' => $percent,
'number' => $number,
'%number' => $percentFromNumber
];
}
}
}
var_dump($results);
Checking the number of combinations with this code (there are 620!) and it takes 3 milliseconds seconds to complete.
I try to compare two swim times in php. They are like HH:MM:SS.XX (XX are hundreths). I get them as string and i want to find out which swimmer is faster. I tryed to convert them using strtotime(). It works with hours, minutes and seconds but it ignores hundreths. Here is my code for better explanation:
$novy = strtotime($input1);
$stary = strtotime($input2);
if($novy < $stary){
//change old(stary) to new(novy)
}
If $input1 is 00:02:14.31 and $input2 is 00:02:14.32 both $novy and $stary are 1392850934.
I read some solution to similar problem in javascript but I can`t use it, this must be server-side.
Thank you for help.
If you use date_create_from_format you can specify the exact date format for php to convert the string representations to:
<?php
$input1 = '00:02:14.31';
$input2 = '00:02:14.32';
$novy = date_create_from_format('H:i:s.u', $input1);
$stary = date_create_from_format('H:i:s.u',$input2);
if ($novy < $stary) {
echo "1 shorter\n";
} else {
echo "2 longer\n";
}
Recommended reading: http://ie2.php.net/datetime.createfromformat
If the format is really HH:MM:SS.XX (ie: with leading 0's), you can just sort them alphabetically:
<?php
$input1 = '00:02:14.31';
$input2 = '00:02:14.32';
if ($input1 < $input2) {
echo "1 faster\n";
} else {
echo "2 faster\n";
}
It prints 1 faster
You could write some conditional logic to test if HH::MM::SS are identical, then simply compare XX, else use the strtotime() function that you are already using
You are working with durations, not dates. PHP's date and time functions aren't really of any help here. You should parse the string yourself to get a fully numeric duration:
$time = '00:02:14.31';
sscanf($time, '%d:%d:%d.%d', $hours, $minutes, $seconds, $centiseconds);
$total = $centiseconds
+ $seconds * 100
+ $minutes * 60 * 100
+ $hours * 60 * 60 * 100;
var_dump($total);
The total is in centiseconds (100th of a second, the scale of your original input). Multiply/divide by other factors to get in others scales, as needed.
time() is in seconds - is there one in milliseconds?
The short answer is:
$milliseconds = floor(microtime(true) * 1000);
Use microtime. This function returns a string separated by a space. The first part is the fractional part of seconds, the second part is the integral part. Pass in true to get as a number:
var_dump(microtime()); // string(21) "0.89115400 1283846202"
var_dump(microtime(true)); // float(1283846202.89)
Beware of precision loss if you use microtime(true).
There is also gettimeofday that returns the microseconds part as an integer.
var_dump(gettimeofday());
/*
array(4) {
["sec"]=>
int(1283846202)
["usec"]=>
int(891199)
["minuteswest"]=>
int(-60)
["dsttime"]=>
int(1)
}
*/
Short answer:
64 bits platforms only!
function milliseconds() {
$mt = explode(' ', microtime());
return intval( $mt[1] * 1E3 ) + intval( round( $mt[0] * 1E3 ) );
}
[ If you are running 64 bits PHP then the constant PHP_INT_SIZE equals to 8 ]
Long answer:
If you want an equilvalent function of time() in milliseconds first you have to consider that as time() returns the number of seconds elapsed since the "epoch time" (01/01/1970), the number of milliseconds since the "epoch time" is a big number and doesn't fit into a 32 bits integer.
The size of an integer in PHP can be 32 or 64 bits depending on platform.
From http://php.net/manual/en/language.types.integer.php
The size of an integer is platform-dependent, although a maximum value of about two billion is the usual value (that's 32 bits signed). 64-bit platforms usually have a maximum value of about 9E18, except for Windows, which is always 32 bit. PHP does not support unsigned integers. Integer size can be determined using the constant PHP_INT_SIZE, and maximum value using the constant PHP_INT_MAX since PHP 4.4.0 and PHP 5.0.5.
If you have 64 bits integers then you may use the following function:
function milliseconds() {
$mt = explode(' ', microtime());
return intval( $mt[1] * 1E3 ) + intval( round( $mt[0] * 1E3 ) );
}
microtime() returns the number of seconds since the "epoch time" with precision up to microseconds with two numbers separated by space, like...
0.90441300 1409263371
The second number is the seconds (integer) while the first one is the decimal part.
The above function milliseconds() takes the integer part multiplied by 1000
1409263371000
then adds the decimal part multiplied by 1000 and rounded to 0 decimals
1409263371904
Note that both $mt[1] and the result of round are casted to int via intval(). This is necessary because they are floats and the operation on them without casting would result in the function returning a float with a loss in precision.
Finally, that function is slightly more precise than
round(microtime(true)*1000);
that with a ratio of 1:10 (approx.) returns 1 more millisecond than the correct result.
This is due to the limited precision of the float type (microtime(true) returns a float).
Anyway if you still prefer the shorter round(microtime(true)*1000); I would suggest casting to int the result.
Even if it's beyond the scope of the question it's worth mentioning that if your platform supports 64 bits integers then you can also get the current time in microseconds without incurring in overflow.
If fact 2^63 - 1 (biggest signed integer) divided by 10^6 * 3600 * 24 * 365 (approximately the microseconds in one year) gives 292471.
That's the same value you get with
echo intdiv( PHP_INT_MAX, 1E6 * 3600 * 24 * 365 );
In other words, a signed 64 bits integer have room to store a timespan of over 200,000 years measured in microseconds.
You may have then
function microseconds() {
$mt = explode(' ', microtime());
return intval( $mt[1] * 1E6 ) + intval( round( $mt[0] * 1E6 ) );
}
As other have stated, you can use microtime() to get millisecond precision on timestamps.
From your comments, you seem to want it as a high-precision UNIX Timestamp. Something like DateTime.Now.Ticks in the .NET world.
You may use the following function to do so:
function millitime() {
$microtime = microtime();
$comps = explode(' ', $microtime);
// Note: Using a string here to prevent loss of precision
// in case of "overflow" (PHP converts it to a double)
return sprintf('%d%03d', $comps[1], $comps[0] * 1000);
}
Shortest version of string variant (32-bit compatibile):
$milliseconds = date_create()->format('Uv');
echo date('Y-m-d H:i:s.') . gettimeofday()['usec'];
output:
2016-11-19 15:12:34.346351
Use microtime(true) in PHP 5, or the following modification in PHP 4:
array_sum(explode(' ', microtime()));
A portable way to write that code would be:
function getMicrotime()
{
if (version_compare(PHP_VERSION, '5.0.0', '<'))
{
return array_sum(explode(' ', microtime()));
}
return microtime(true);
}
This works even if you are on 32-bit PHP:
list($msec, $sec) = explode(' ', microtime());
$time_milli = $sec.substr($msec, 2, 3); // '1491536422147'
$time_micro = $sec.substr($msec, 2, 6); // '1491536422147300'
Note this doesn't give you integers, but strings. However this works fine in many cases, for example when building URLs for REST requests.
If you need integers, 64-bit PHP is mandatory.
Then you can reuse the above code and cast to (int):
list($msec, $sec) = explode(' ', microtime());
// these parentheses are mandatory otherwise the precedence is wrong!
// ↓ ↓
$time_milli = (int) ($sec.substr($msec, 2, 3)); // 1491536422147
$time_micro = (int) ($sec.substr($msec, 2, 6)); // 1491536422147300
Or you can use the good ol' one-liners:
$time_milli = (int) round(microtime(true) * 1000); // 1491536422147
$time_micro = (int) round(microtime(true) * 1000000); // 1491536422147300
try this:
public function getTimeToMicroseconds() {
$t = microtime(true);
$micro = sprintf("%06d", ($t - floor($t)) * 1000000);
$d = new DateTime(date('Y-m-d H:i:s.' . $micro, $t));
return $d->format("Y-m-d H:i:s.u");
}
PHP 5.2.2 <
$d = new DateTime();
echo $d->format("Y-m-d H:i:s.u"); // u : Microseconds
PHP 7.0.0 < 7.1
$d = new DateTime();
echo $d->format("Y-m-d H:i:s.v"); // v : Milliseconds
$timeparts = explode(" ",microtime());
$currenttime = bcadd(($timeparts[0]*1000),bcmul($timeparts[1],1000));
echo $currenttime;
NOTE: PHP5 is required for this function due to the improvements with
microtime() and the bc math module is also required (as we’re dealing
with large numbers, you can check if you have the module in phpinfo).
Hope this help you.
$the_date_time = new DateTime($date_string);
$the_date_time_in_ms = ($the_date_time->format('U') * 1000) +
($the_date_time->format('u') / 1000);
This is my implementation, should work on 32bit as well.
function mstime(){
$mstime = explode(' ',microtime());
return $mstime[1].''.(int)($mstime[0]*1000);
}
If you want to see real microseconds, you will need to change the precision setting in php.ini to 16.
After that, microsecond(true) gave me the output of 1631882476.298437.
So I thought that I need to divide the remainder (298437) with 1000, but in fact, the remainder is 0.298437 of a second. So I need to multiply that by 1000 to get the correct result.
function get_milliseconds()
{
$timestamp = microtime(true);
return (int)(($timestamp - (int)$timestamp) * 1000);
}
I personaly use this:
public static function formatMicrotimestamp(DateTimeInterface $dateTime): int
{
return (int) substr($dateTime->format('Uu'), 0, 13);
}
Use this:
function get_millis(){
list($usec, $sec) = explode(' ', microtime());
return (int) ((int) $sec * 1000 + ((float) $usec * 1000));
}
Bye
I need to convert coordinates in the following form:
N42-53.9°
W072-16.2°
Into something that is like the following:
-90.7311
0.346944
A php function would be greatly appreciated - or just a formula would also be nice enough.
I found an online JS calculator and a PHP solution:
<?php
function DMStoDEC($deg,$min,$sec)
{
// Converts DMS ( Degrees / minutes / seconds )
// to decimal format longitude / latitude
return $deg+((($min*60)+($sec))/3600);
}
function DECtoDMS($dec)
{
// Converts decimal longitude / latitude to DMS
// ( Degrees / minutes / seconds )
// This is the piece of code which may appear to
// be inefficient, but to avoid issues with floating
// point math we extract the integer part and the float
// part by using a string function.
$vars = explode(".",$dec);
$deg = $vars[0];
$tempma = "0.".$vars[1];
$tempma = $tempma * 3600;
$min = floor($tempma / 60);
$sec = $tempma - ($min*60);
return array("deg"=>$deg,"min"=>$min,"sec"=>$sec);
}
?>
Do it with simple mathematics:
arcsecond is 1⁄3,600 of a degree
arcminute is 1/60 of a degree
S,E negative, N,W positive
example: S 23° 25' 33.8: -1 * 23+25/60+33.8/3600 = -23,426055555555555555555555555556°
I have a part in my code where I need to divide and have a remainder instead of a decimal answer.
How can I do this?
$quotient = intval($dividend / $divisor);
$remainder = $dividend % $divisor;
Using intval instead of floor will round the quotient towards zero, providing accurate results when the dividend is negative.
You can do what you are describing using the "%" (modulus) operator. The following code is an example of dividing with a remainder.
$remainder=$num % $divideby;
$number=explode('.',($num / $divideby));
$answer=$number[0];
echo $answer.' remainder '.$remainder;
A solution for positive and negative numbers:
$quotient = $dividend / $divison;
$integer = (int) ($quotient < 0 ? ceil($quotient) : floor($quotient));
$remainder = $dividend % $divisor;
The mathematical correct answer is:
remainder = dividend % divisor;
quotient = (dividend - remainder) / divisor;
and the remainder verifies the condition 0 <= remainder < abs(divisor).
Unfortunately, many programming languages (including PHP) don't handle the negative numbers correctly from the mathematical point of view. They use different rules to compute the value and the sign of the remainder. The code above does not produce the correct results in PHP.
If you need to work with negative numbers and get the mathematical correct results using PHP then you can use the following formulae:
$remainder = (($dividend % $divider) + abs($divider)) % abs($divider);
$quotient = ($dividend - $remainder) / $divider;
They rely on the way PHP computes modulus with negative operands and they may not provide the correct result if they are ported to a different language.
Here is a script that implements these formulae and checks the results against the values provided as example in the aforementioned mathematical correct answer.
If you need to look it up, the % operator is called mod (or modulus).
I had to develop this approach because my numerator was a float value and modulus was rounding results.
Using Raffaello's approach offered here for dividing floats and taking from Sam152's solution above came up with the following.
$a = 2.1;
$b = 8;
$fraction = $a / (float) $b;
$parts = explode('.', $fraction);
$int = $parts[0];
$remainder = $score - ($int*$b) ;
Use This Function Its an array
Description
array gmp_div_qr ( resource $n , resource $d [, int $round ] )
The function divides n by d .
reference : http://php.net/manual/en/function.gmp-div-qr.php
An example to show strings like 1 hour 6 minutes using floor() and modulus (%) if only minutes/seconds given:
$minutes=126;
if($minutes < 60) {
$span= $minutes.' min.';
} else {
$rem=$minutes % 60;
$span=floor($minutes/60).' h. '. (($rem>0) ? $rem.' min.':'');
}
// echo 'Hello Jon Doe, we notify you that even will last for 2 h. 6 min.
echo 'Hello Jon Doe, we notify you that event will last for '.$span;
It seems to be an old post, but for those who might be interested here is a very light package that could meet your needs: https://github.com/romainnorberg/residue (feedbacks are welcome)