我最终使用了一次打印整个坐标的解决方案,因为我有大量的舍入误差分别处理度/分/秒(例如,2.0° 被打印为 1°60')。它的灵感来自 vitaut 将整数和小数部分格式化为单独的字符串的解决方案:
// value is the input in decimal degrees (e.g. 77° 20' = 77.3333333)
// deg_width controls if degrees are printed with 2 (latitude) or 3 (longitude) digits
std::string formatDMS (double value, size_t deg_width)
{
std::string res;
static const int SECONDS_DECIMAL_PLACES = 2; // amount of decimals to print seconds with
// Convert everything to int, to get rid of pesky floating-point errors
static const int ONE_SECOND = std::pow (10, SECONDS_DECIMAL_PLACES); // e.g. 1 second = 100 * 0.01 (seconds with 2 decimals)
static const int ONE_MINUTE = 60 * ONE_SECOND; // e.g. 1 minute = 6000 * 0.01 (seconds with 2 decimals)
static const int ONE_DEGREE = 60 * ONE_MINUTE; // e.g. 1 minute = 360000 * 0.01 (seconds with 2 decimals)
const int value_incs = std::lround (value * ONE_DEGREE);
const int degrees = value_incs / ONE_DEGREE;
const int deg_rem = value_incs % ONE_DEGREE;
const int minutes = deg_rem / ONE_MINUTE;
const int min_rem = deg_rem % ONE_MINUTE;
const int seconds = min_rem / ONE_SECOND;
const int sec_rem = min_rem % ONE_SECOND;
const double decimals = static_cast<double>(sec_rem) / ONE_SECOND;
const auto decimals_string =
fmt::format ("{:.{}g}", decimals, SECONDS_DECIMAL_PLACES).substr (1);
const auto fmtstring = "{:0{}d}° {:02d}\' {:02d}{}\"";
res += fmt::format (fmtstring, degrees, deg_width, minutes, seconds, decimals_string);
return res;
}
formatDMS(77.0, 2); // 77°00'00"
formatDMS(77.033333333, 2); // 77°02'00"
formatDMS(77.049722222, 2); // 77°02'59"
formatDMS(77.049888889, 2); // 77°02'59.6"
formatDMS(77.999999999, 2); // 78°00'00"
formatDMS(7.0, 3); // 007°00'00"
formatDMS(7.2, 3); // 007°12'00"
formatDMS(7.203333333, 3); // 007°12'12"
formatDMS(7.203366667, 3); // 007°12'12.12"
formatDMS(7.999999999, 3); // 008°00'00"