如何解决double和float精度不准的问题?

本文探讨了float和double类型在二进制表示下存在的精度问题,通过实例展示了这些类型的计算误差,并介绍了如何利用BigDecimal类来解决精度问题,确保数值计算的准确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

float和double型,的底层实现是二进制的。十进制中的一个有限位数小数,转换成二进制就不一定是有限位数了,一旦位数超过的float和double型的位数宽度,就会出现“精度溢出”。所以float和double型是为了科学计算而设计的,并不适合精确的十进制计算.

就像一个十进制的小数,要不断地乘以2取整,但在这个过程中可能会一直循环下去,这就造成了数据的不精确。

所以在必须要求数据的精确度时,不能使用float和double.

public class Test{

public static void main(String[] args)

{

System.out.println(0.05+0.01);

System.out.println(1.0-0.42);

System.out.println(4.015*100);

System.out.println(123.3/100);

}

}

输出结果为:

0.060000000000000005

0.5800000000000001

401.49999999999994

1.2329999999999999

BigDecimal类可解决计算精度问题可使用BigDecimal类创建一个封装类。封装加减乘除操作

例:对一个小数进行指定位数的四舍五入:

BigDecimal bd = new BigDecimal("0.9851095");

BigDecimal one = new BigDecimal("1");

System.out.println(bd.divide(one, 3, BigDecimal.ROUND_HALF_UP));

BigDecimal中还有很多相关的数值之间的计算方法,以及精确到的位数和四舍五入等

void approxPolyDPTest(const cv::Mat img, const cv::Mat ROI_img, std::vector<cv::Point> maxContour) { std::vector<cv::Point> approx; double epsilon = 0.02 * arcLength(maxContour, true); approxPolyDP(maxContour, approx, epsilon, true); cv::Point topLeft; int minSum = INT_MAX; for (const auto& p : approx) { int currentSum = p.x + p.y; if (currentSum < minSum) { minSum = currentSum; topLeft = p; } } cv::circle(img, topLeft, 5, cv::Scalar(0, 0, 255), -1); // 标记左上角点 cv::Rect roiRect(topLeft.x - 100, topLeft.y+10 , 200, 300); if (roiRect.x < 0 || roiRect.y < 0 || roiRect.br().x > ROI_img.cols || roiRect.br().y > ROI_img.rows) { std::cerr << "错误:ROI超出图像范围" << std::endl; } cv::Mat roi = ROI_img(roiRect); //std::vector<cv::Vec4i> lines; ////cv::HoughLinesP(binaryImage, lines, 1, CV_PI / 180, 50, 50, 10); //cv::HoughLinesP(roi, lines, 1, CV_PI / 180, 10, 50, 10); std::vector<std::vector<cv::Point>> basic_contours; cv::findContours(roi, basic_contours, cv::RETR_LIST, cv::CHAIN_APPROX_NONE); // --- 关键改进区域 start --- // 1. 寻找有效轮廓(尺寸排序) std::vector<cv::Point> lineContour; std::vector<double> contourSizes; for (const auto& contour : basic_contours) { if (contour.size() > 5) { // 忽略小噪点 contourSizes.push_back(cv::contourArea(contour)); } } // 2. 选择最大轮廓(使用面积更可靠) if (!contourSizes.empty()) { auto maxIt = std::max_element(contourSizes.begin(), contourSizes.end()); size_t idx = std::distance(contourSizes.begin(), maxIt); lineContour = basic_contours[idx]; } // 3. 确保点数据可用 if (lineContour.empty() || lineContour.size() < 2) { std::cerr << "错误:有效轮廓点不足" << std::endl; return; } // 4. 格式转换:Point → Point2f(OpenCV必需) std::vector<cv::Point2f> points2f; points2f.reserve(lineContour.size()); for (const auto& pt : lineContour) { points2f.emplace_back(pt); std::cout << "point.x " << pt.x << " , " << "point.y " << pt.y << std::endl; } // 5. 调用fitLine(优化参数) cv::Vec4f lineParams; // 使用float精度(更合理的类型) cv::fitLine( points2f, // 输入:点向量 lineParams, // 输出:[vx, vy, x0, y0]单位向量+直线上点 cv::DIST_L2, // 距离算法 0, // L2不需要参数 0.01, // 半径精度(推荐值) 0.01 // 角度精度(推荐值) ); // --- 关键改进区域 end --- // 使用拟合结果:绘制直线 float vx = lineParams[0], vy = lineParams[1]; float x0 = lineParams[2], y0 = lineParams[3]; float k = vy / vx; // 斜率 float b = y0 - k * x0; // 计算线段起点终点(在ROI内) cv::Point pt1(0, static_cast<int>(b)); cv::Point pt2(roi.cols, static_cast<int>(k * roi.cols + b)); cv::line(roi, pt1, pt2, cv::Scalar(0, 255, 0), 2); // ... [原始绘制逻辑] ... // 调试标记(左上角点) cv::circle(img, topLeft, 5, cv::Scalar(0, 0, 255), -1); std::vector<std::vector<cv::Point>> drawingContour; drawingContour.push_back(approx); cv::drawContours(img, drawingContour, -1, cv::Scalar(0, 255, 0), 1); } 优化一下 目前问题是 cv::fitLine( points2f, // 输入:点向量 lineParams, // 输出:[vx, vy, x0, y0]单位向量+直线上点 cv::DIST_L2, // 距离算法 0, // L2不需要参数 0.01, // 半径精度(推荐值) 0.01 // 角度精度(推荐值) );拟合的线不准 具体数据详见前一个问题
最新发布
07-01
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值