You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用boost::units结合scaled_unit与si::unit计算体积?

问题描述

我需要通过面积×高度计算得到单位为mm³的体积,但boost::units没有内置mm²单位,于是尝试用make_scaled_unit自定义单位。参照资料实现后,在boost的quantity.hpp中触发错误:

C2338 (is_implicitly_convertible<Unit2,unit_type>::value == true).

同时伴随包含复杂类型名称的模板实例化提示,错误指向contentVolume赋值语句,代码如下:

#include <iostream>
#include <boost/units/systems/si/volume.hpp>
#include <boost/units/systems/si/mass.hpp>
#include "string"
#include <boost/units/systems/si/io.hpp>
#include <boost/units/systems/si/prefixes.hpp>
#include <boost/units/systems/angle/degrees.hpp>
#include <cmath>

using namespace boost::units;
using namespace boost::units::si;
using Volume = boost::units::quantity<boost::units::si::volume>;
using Square = boost::units::quantity<boost::units::area_dimension>;

// like in stackoverflow answer
namespace extended_area_names {
namespace squared_millimeters_system
{
    // "area" is not a base unit
    typedef make_scaled_unit<area, scale<10, static_rational<-6>>>::type millimeter_unit;
    typedef make_system<millimeter_unit>::type system;
    typedef unit<area_dimension, system> area;

    BOOST_UNITS_STATIC_CONSTANT(squared_millimeter, area);
    BOOST_UNITS_STATIC_CONSTANT(squared_millimeters, area);
} // namespace squared_millimeters_system

typedef quantity<squared_millimeters_system::area> quantity_area_square_millimeter;
using squared_millimeters_system::squared_millimeter;
using squared_millimeters_system::squared_millimeters;
} // namespace extended_area_names

    // like in boost documentation, I use only one of these per try
typedef make_scaled_unit<area, scale<10, static_rational<-6>>>::type millimeter_unit;

Volume ContentVolume(std::string tankType, double contentLevel_raw, double tankHeight_raw, double tankDiameter_raw)
{
quantity<length> contentLevel(contentLevel_raw * milli * meters);
quantity<length> tankHeight(tankHeight_raw * milli * meters);
quantity<length> tankDiameter(tankDiameter_raw * milli * meters);
quantity<length> tankRadius = tankDiameter / 2.0;
Volume tankVolume(3.1415 * (tankRadius * tankRadius) * tankHeight);
Volume contentVolume;


using namespace extended_area_names;

quantity
    <unit<plane_angle_dimension, degree::system>
    > sectorAngle(2 * acos((tankRadius - tankHeight) / tankRadius)
        * degree::degree);

quantity<millimeter_unit> sectorSquare(
    (sectorAngle.value() * (tankRadius.value() * tankRadius.value()) / 2.0)
    * square_meter);

quantity<length> triangleBase(
    sqrt(
        (tankRadius.value() * tankRadius.value()) -
        ((tankRadius.value() - tankHeight.value()) *
            (tankRadius.value() - tankHeight.value())))
    * milli * meters);

quantity<length> p(
    (tankRadius + tankRadius + triangleBase) / 2.0);

quantity<millimeter_unit> triangleSquare(
    sqrt(
        p.value() *
        (p.value() - tankRadius.value()) *
        (p.value() - tankRadius.value()) *
        (p.value() - triangleBase.value()))
    * square_meter);

// the message says the problem is here
contentVolume = (sectorSquare - triangleSquare) * tankHeight;

return contentVolume; //m^3
}
}
}
修复方案

核心问题

自定义的独立单位系统与boost内置SI系统不兼容,导致类型无法隐式转换;同时直接操作数值丢失了单位上下文,引发类型不匹配。

具体修复步骤

  1. 复用SI系统定义平方毫米
    不需要创建新的单位系统,直接基于SI的area单位缩放,保证兼容性:

    typedef make_scaled_unit<si::area, scale<10, static_rational<-6>>>::type square_millimeter;
    BOOST_UNITS_STATIC_CONSTANT(square_millimeters, square_millimeter);
    
  2. 避免直接提取数值value()
    用boost::units的运算符直接计算,让库自动处理单位转换,不要手动剥离单位。

  3. 修正角度单位处理
    boost的三角函数默认接受弧度,需将角度转换为弧度后再计算。

修正后的完整代码

#include <iostream>
#include <boost/units/systems/si/volume.hpp>
#include <boost/units/systems/si/mass.hpp>
#include <string>
#include <boost/units/systems/si/io.hpp>
#include <boost/units/systems/si/prefixes.hpp>
#include <boost/units/systems/angle/degrees.hpp>
#include <boost/units/conversion.hpp>
#include <cmath>

using namespace boost::units;
using namespace boost::units::si;

// 基于SI系统定义平方毫米,无需新建单位系统
typedef make_scaled_unit<si::area, scale<10, static_rational<-6>>>::type square_millimeter;
BOOST_UNITS_STATIC_CONSTANT(square_millimeters, square_millimeter);

using Volume = quantity<si::volume>;
using Area = quantity<si::area>;
using SquareMillimeterArea = quantity<square_millimeter>;

Volume ContentVolume(std::string tankType, double contentLevel_raw, double tankHeight_raw, double tankDiameter_raw)
{
    quantity<length> contentLevel(contentLevel_raw * milli * meters);
    quantity<length> tankHeight(tankHeight_raw * milli * meters);
    quantity<length> tankDiameter(tankDiameter_raw * milli * meters);
    quantity<length> tankRadius = tankDiameter / 2.0;
    Volume tankVolume(3.1415 * tankRadius * tankRadius * tankHeight);
    Volume contentVolume;

    // 计算扇形角度,转换为弧度(boost三角函数默认用弧度)
    quantity<plane_angle> sector_angle_rad = 2 * acos((tankRadius - tankHeight) / tankRadius);
    
    // 直接用单位计算扇形面积,无需手动提取数值
    Area sector_area = (sector_angle_rad * tankRadius * tankRadius) / 2.0;
    
    // 计算三角形底边长
    quantity<length> triangle_base = sqrt(tankRadius*tankRadius - (tankHeight - tankRadius)*(tankHeight - tankRadius));
    
    // 海伦公式计算三角形面积
    quantity<length> p = (tankRadius + tankRadius + triangle_base) / 2.0;
    Area triangle_area = sqrt(p * (p - tankRadius) * (p - tankRadius) * (p - triangle_base));
    
    // 计算内容物体积,自动处理单位转换
    contentVolume = (sector_area - triangle_area) * tankHeight;

    // 如果需要输出mm³格式,可以转换后输出
    SquareMillimeterArea sector_area_mm2 = sector_area;
    SquareMillimeterArea triangle_area_mm2 = triangle_area;
    quantity<make_scaled_unit<si::volume, scale<10, static_rational<-9>>>::type> content_volume_mm3 = contentVolume;
    std::cout << "内容物体积(mm³): " << content_volume_mm3 << std::endl;

    return contentVolume; // 返回m³单位的体积
}

int main() {
    // 测试调用
    Volume vol = ContentVolume("cylinder", 500.0, 1000.0, 800.0);
    std::cout << "内容物体积(m³): " << vol << std::endl;
    return 0;
}

内容的提问来源于stack exchange,提问作者Ted Denissenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 22:36:20