Я знаю формуляр для преобразования от степени к Miliseconds и наоборот. Он может быть реализован следующим образом:
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; }
Сценарий: я конвертирую из Degree в Miliseconds и продолжаю конвертировать из Miliseconds в Degree. Преобразованное значение имеет некоторую разницу с исходным значением. Я хочу, чтобы значение было точным, как оригинальное значение. Например:
$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)
Есть ли другой способ уменьшить разницу, вызванный конверсией между степенью и милисекундами? Спасибо.
Есть 360 градусов (долгота), 60 минут на градус, 60 секунд в минуту, 1000 миллисекунд в секунду. Так что самое большее
360*60*60*1000 milliseconds = 1 296 000 000 milliseconds
Это хорошо подходит для 31 бит, поэтому идея состояла бы в том, чтобы сначала преобразовать в целое число и выполнить как можно больше операций в целых числах.
Обратите внимание: если вы используете одинаковую точность с плавающей запятой, вы получите значение в 24 бита и потеряете точность до 1 десятой секунды ( log2(360*60*60*10)
составляет около 23.6
).
Я бы рекомендовал хранить результаты с двойной точностью (53 бит).
РЕДАКТИРОВАТЬ
То, что я предлагаю, – это выполнить преобразование сразу, если есть способ использовать двойную точность для представления $decimaldegrees
(я не знаю, php достаточно, чтобы сказать это), что-то вроде:
$millis = (int)( round( $decimaldegrees * (60*60*1000) ) );
Затем, если вы хотите разложить на DMS (но эти переменные не используются в вашем коде):
$ms = $millis % 1000; $sec = ($millis / 1000) % 60; $min = ($millis / (60*1000)) % 60; $deg = ($millis / (60*60*1000)) % 360;
Более глубокий взгляд на ваш код, кажется, вы отделяете десятичную часть сначала $tempma = "0.".$vars[1];
Это может работать, если вы работаете с строкой десятичного представления, потому что в этом случае это хорошо подходит даже для одного прецизионного поплавка ( log2(60*60*1000)
составляет около 21.8
). Таким образом, начало можно заменить:
$deg = (int) $vars[0]; $frac = "0.".$vars[1]; $millis = (int)( round( $frac * (60*60*1000) ) ); $millis = deg + millis;
Из примера вывода, который вы дали, похоже, что проблема исходит из другого преобразования milisecond_to_decimal
, предположительно потому, что некоторая арифметическая операция выполняется с целочисленной арифметикой и, таким образом, отбрасывает миллисекунды.
Еще раз, я не знаю php достаточно, но не $s = $s % 3600;
фактически действуют на (int)(%s)
и, таким образом, отбрасывают миллисекунды?
Вам нужно будет найти что-то эквивалентное функции C modf
или modf
.
Еще раз, вы можете сделать все операции сразу, если есть способ сделать это с двойной точностью:
$decimaldegrees = ((double)($millis)) / (60*60*1000);
Если у вас нет доступа к двойной точности, вы не можете перекомпоновать безопасно, у одной точности не хватает бит …
Вам нужно будет работать на отдельных частях струны, заботясь о ведущих нулях во фракционной части
Во всяком случае, я настоятельно рекомендую выполнить модульные тесты, то есть проверить ваши две функции отдельно, таким образом вы сможете лучше понять, что работает, а что нет.