changeset 6577:344ae6b5db9b

workaround for the incompatibility of boost::math with MSVC <= 2015
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 27 Jan 2026 16:07:48 +0100
parents c621863916df
children 0d650504d32c
files OrthancFramework/Sources/CompatibilityMath.h OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.cpp OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp OrthancFramework/Sources/Images/ImageProcessing.cpp OrthancFramework/Sources/JobsEngine/JobInfo.cpp OrthancFramework/Sources/MetricsRegistry.cpp OrthancFramework/Sources/RestApi/RestApi.cpp OrthancFramework/UnitTestsSources/FrameworkTests.cpp OrthancServer/Plugins/Engine/OrthancPlugins.cpp OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp
diffstat 10 files changed, 325 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancFramework/Sources/CompatibilityMath.h	Tue Jan 27 16:07:48 2026 +0100
@@ -0,0 +1,250 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2023 Osimis S.A., Belgium
+ * Copyright (C) 2024-2026 Orthanc Team SRL, Belgium
+ * Copyright (C) 2021-2026 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#if defined(BOOST_MATH_ROUND_HPP)
+#  error You should not manually include <boost/math/special_functions/round.hpp>
+#endif
+
+#include "Compatibility.h"
+
+
+#ifdef __EMSCRIPTEN__
+/*
+   Avoid such errors:
+   ------------------
+
+   .../boost/math/special_functions/round.hpp:118:12: warning: implicit conversion from 'std::__2::numeric_limits<long long>::type' (aka 'long long') to 'float' changes value from 9223372036854775807 to 9223372036854775808 [-Wimplicit-int-float-conversion]
+   .../boost/math/special_functions/round.hpp:125:11: note: in instantiation of function template specialization 'boost::math::llround<float, boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy> >' requested here
+
+   .../boost/math/special_functions/round.hpp:86:12: warning: implicit conversion from 'std::__2::numeric_limits<int>::type' (aka 'int') to 'float' changes value from 2147483647 to 2147483648 [-Wimplicit-int-float-conversion]
+   .../boost/math/special_functions/round.hpp:93:11: note: in instantiation of function template specialization 'boost::math::iround<float, boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy> >' requested here
+*/
+#  pragma GCC diagnostic ignored "-Wimplicit-int-float-conversion"
+#endif
+
+
+#if 1 || defined(_MSC_VER)
+
+/**
+ * This is for compatibility with Microsoft Visual Studio <= 2015.
+ * Indeed, Boost 1.89.0 does not support MSVC 14.0, as can be seen in
+ * this file:
+ * https://www.boost.org/doc/libs/1_89_0/boost/math/tools/config.hpp
+ **/
+
+#include <cmath>
+#include <limits>
+
+namespace Orthanc
+{
+  namespace Math
+  {
+    namespace Internals
+    {
+      ORTHANC_FORCE_INLINE
+      float RoundFloat(float v)
+      {
+        if (v >= 0.0f)
+        {
+          return std::floor(v + 0.5f);
+        }
+        else
+        {
+          return std::ceil(v - 0.5f);
+        }
+      }
+
+      ORTHANC_FORCE_INLINE
+      double RoundDouble(double v)
+      {
+        if (v >= 0.0)
+        {
+          return std::floor(v + 0.5);
+        }
+        else
+        {
+          return std::ceil(v - 0.5);
+        }
+      }
+
+      template <typename T>
+      ORTHANC_FORCE_INLINE
+      bool RoundFloatWithChecks(T& target,
+                                float source)
+      {
+        if (std::isnan(source) ||
+            !std::isfinite(source))
+        {
+          return false;
+        }
+        else
+        {
+          float rounded = RoundFloat(source);
+          if (rounded < static_cast<float>(std::numeric_limits<T>::min()) ||
+              rounded > static_cast<float>(std::numeric_limits<T>::max()))
+          {
+            return false;
+          }
+          else
+          {
+            target = static_cast<T>(rounded);
+            return true;
+          }
+        }
+      }
+
+      template <typename T>
+      ORTHANC_FORCE_INLINE
+      bool RoundDoubleWithChecks(T& target,
+                                 double source)
+      {
+        if (std::isnan(source) ||
+            !std::isfinite(source))
+        {
+          return false;
+        }
+        else
+        {
+          double rounded = RoundDouble(source);
+          if (rounded < static_cast<double>(std::numeric_limits<T>::min()) ||
+              rounded > static_cast<double>(std::numeric_limits<T>::max()))
+          {
+            return false;
+          }
+          else
+          {
+            target = static_cast<T>(rounded);
+            return true;
+          }
+        }
+      }
+    }
+
+    ORTHANC_FORCE_INLINE
+    int iround(float v)
+    {
+      return static_cast<int>(Internals::RoundFloat(v));
+    }
+
+    ORTHANC_FORCE_INLINE
+    int iround(double v)
+    {
+      return static_cast<int>(Internals::RoundDouble(v));
+    }
+
+    ORTHANC_FORCE_INLINE
+    long long llround(float v)
+    {
+      return static_cast<long long>(Internals::RoundFloat(v));
+    }
+
+    ORTHANC_FORCE_INLINE
+    long long llround(double v)
+    {
+      return static_cast<long long>(Internals::RoundDouble(v));
+    }
+
+    ORTHANC_FORCE_INLINE
+    bool llround(long long& target,
+                 float source)
+    {
+      return Internals::RoundFloatWithChecks<long long>(target, source);
+    }
+
+    ORTHANC_FORCE_INLINE
+    long long llround(long long& target,
+                      double source)
+    {
+      return Internals::RoundDoubleWithChecks<long long>(target, source);
+    }
+  }
+}
+
+#else
+
+#include <boost/math/special_functions/round.hpp>
+
+namespace Orthanc
+{
+  namespace Math
+  {
+    ORTHANC_FORCE_INLINE
+    int iround(float v)
+    {
+      return boost::math::iround<float>(v);
+    }
+
+    ORTHANC_FORCE_INLINE
+    int iround(double v)
+    {
+      return boost::math::iround<double>(v);
+    }
+
+    ORTHANC_FORCE_INLINE
+    long long llround(float v)
+    {
+      return boost::math::llround<float>(v);
+    }
+
+    ORTHANC_FORCE_INLINE
+    long long llround(double v)
+    {
+      return boost::math::llround<double>(v);
+    }
+
+    ORTHANC_FORCE_INLINE
+    bool llround(long long& target,
+                 float source)
+    {
+      try
+      {
+        target = boost::math::llround<float>(source);
+        return true;
+      }
+      catch (boost::math::rounding_error&)
+      {
+        return false;
+      }
+    }
+
+    ORTHANC_FORCE_INLINE
+    long long llround(long long& target,
+                      double source)
+    {
+      try
+      {
+        target = boost::math::llround<double>(source);
+        return true;
+      }
+      catch (boost::math::rounding_error&)
+      {
+        return false;
+      }
+    }
+  }
+}
+
+#endif
--- a/OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.cpp	Tue Jan 27 13:15:50 2026 +0100
+++ b/OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.cpp	Tue Jan 27 16:07:48 2026 +0100
@@ -25,13 +25,13 @@
 #include "../PrecompiledHeaders.h"
 #include "DicomWebJsonVisitor.h"
 
+#include "../CompatibilityMath.h"
 #include "../Logging.h"
 #include "../OrthancException.h"
+#include "../SerializationToolbox.h"
 #include "../Toolbox.h"
-#include "../SerializationToolbox.h"
 #include "FromDcmtkBridge.h"
 
-#include <boost/math/special_functions/round.hpp>
 #include <boost/lexical_cast.hpp>
 
 
@@ -329,10 +329,9 @@
     
   Json::Value DicomWebJsonVisitor::FormatDouble(double value)
   {
-    try
+    long long a;
+    if (Math::llround(a, value))
     {
-      long long a = boost::math::llround<double>(value);
-
       double d = fabs(value - static_cast<double>(a));
 
       if (d <= std::numeric_limits<double>::epsilon() * 100.0)
@@ -344,7 +343,7 @@
         return Json::Value(value);
       }
     }
-    catch (boost::math::rounding_error&)
+    else
     {
       // Can occur if "long long" is too small to receive this value
       // (e.g. infinity)
@@ -352,12 +351,12 @@
     }
   }
 
-  Json::Value DicomWebJsonVisitor::FormatDecimalString(double value, const std::string& originalString)
+  Json::Value DicomWebJsonVisitor::FormatDecimalString(double value,
+                                                       const std::string& originalString)
   {
-    try
+    long long a;
+    if (Math::llround(a, value))
     {
-      long long a = boost::math::llround<double>(value);
-
       double d = fabs(value - static_cast<double>(a));
 
       if (d <= std::numeric_limits<double>::epsilon() * 100.0)
@@ -369,7 +368,7 @@
         return Json::Value(originalString);  // keep the original string to avoid rounding errors e.g, transforming "0.143" into 0.14299999999999
       }
     }
-    catch (boost::math::rounding_error&)
+    else
     {
       // Can occur if "long long" is too small to receive this value
       // (e.g. infinity)
--- a/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp	Tue Jan 27 13:15:50 2026 +0100
+++ b/OrthancFramework/Sources/DicomParsing/ParsedDicomFile.cpp	Tue Jan 27 16:07:48 2026 +0100
@@ -138,7 +138,6 @@
 #include <dcmtk/dcmdata/dcpxitem.h>
 
 
-#include <boost/math/special_functions/round.hpp>
 #include <dcmtk/dcmdata/dcostrmb.h>
 #include <boost/algorithm/string/predicate.hpp>
 
--- a/OrthancFramework/Sources/Images/ImageProcessing.cpp	Tue Jan 27 13:15:50 2026 +0100
+++ b/OrthancFramework/Sources/Images/ImageProcessing.cpp	Tue Jan 27 16:07:48 2026 +0100
@@ -25,26 +25,15 @@
 #include "../PrecompiledHeaders.h"
 #include "ImageProcessing.h"
 
+#include "../CompatibilityMath.h"
+#include "../OrthancException.h"
 #include "Image.h"
 #include "ImageTraits.h"
 #include "PixelTraits.h"
-#include "../OrthancException.h"
-
-#ifdef __EMSCRIPTEN__
-/* 
-   Avoid this error:
-   -----------------
-   .../boost/math/special_functions/round.hpp:118:12: warning: implicit conversion from 'std::__2::numeric_limits<long long>::type' (aka 'long long') to 'float' changes value from 9223372036854775807 to 9223372036854775808 [-Wimplicit-int-float-conversion]
-   .../mnt/c/osi/dev/orthanc/Core/Images/ImageProcessing.cpp:333:28: note: in instantiation of function template specialization 'boost::math::llround<float>' requested here
-   .../mnt/c/osi/dev/orthanc/Core/Images/ImageProcessing.cpp:1006:9: note: in instantiation of function template specialization 'Orthanc::MultiplyConstantInternal<unsigned char, true>' requested here
-*/
-#pragma GCC diagnostic ignored "-Wimplicit-int-float-conversion"
-#endif 
-
-#include <boost/math/special_functions/round.hpp>
 
 #include <algorithm>
 #include <cassert>
+#include <cmath>
 #include <limits>
 #include <list>
 #include <map>
@@ -384,7 +373,7 @@
         {
           assert(sizeof(long long) == sizeof(int64_t));
           // The "round" operation is very costly
-          v = boost::math::llround(static_cast<float>(*p) * factor);
+          v = Math::llround(static_cast<float>(*p) * factor);
         }
         else
         {
@@ -464,7 +453,7 @@
         {         
           // The "round" operation is very costly
           assert(sizeof(TargetType) < sizeof(int));
-          *p = static_cast<TargetType>(boost::math::iround(v));
+          *p = static_cast<TargetType>(Math::iround(v));
         }
         else
         {
@@ -2716,7 +2705,7 @@
             if (UseRound)
             {
               assert(sizeof(RawPixel) < sizeof(int));
-              *p = static_cast<RawPixel>(boost::math::iround(accumulator));
+              *p = static_cast<RawPixel>(Math::iround(accumulator));
             }
             else
             {
@@ -2868,8 +2857,8 @@
       static_cast<float>(target.GetWidth()) / cw,
       static_cast<float>(target.GetHeight()) / ch);
 
-    unsigned int sw = std::min(static_cast<unsigned int>(boost::math::iround(cw * r)), target.GetWidth());  
-    unsigned int sh = std::min(static_cast<unsigned int>(boost::math::iround(ch * r)), target.GetHeight());
+    unsigned int sw = std::min(static_cast<unsigned int>(Math::iround(cw * r)), target.GetWidth());
+    unsigned int sh = std::min(static_cast<unsigned int>(Math::iround(ch * r)), target.GetHeight());
 
     Image resized(target.GetFormat(), sw, sh, false);
   
@@ -2913,10 +2902,10 @@
                              static_cast<float>(height) / static_cast<float>(source.GetHeight()));
 
       unsigned int resizedWidth = static_cast<unsigned int>(
-        boost::math::iround(ratio * static_cast<float>(source.GetWidth())));
+        Math::iround(ratio * static_cast<float>(source.GetWidth())));
 
       unsigned int resizedHeight = static_cast<unsigned int>(
-        boost::math::iround(ratio * static_cast<float>(source.GetHeight())));
+        Math::iround(ratio * static_cast<float>(source.GetHeight())));
 
       std::unique_ptr<ImageAccessor> resized(FitSize(source, resizedWidth, resizedHeight));
 
--- a/OrthancFramework/Sources/JobsEngine/JobInfo.cpp	Tue Jan 27 13:15:50 2026 +0100
+++ b/OrthancFramework/Sources/JobsEngine/JobInfo.cpp	Tue Jan 27 16:07:48 2026 +0100
@@ -24,27 +24,11 @@
 
 #include "../PrecompiledHeaders.h"
 
-#ifdef __EMSCRIPTEN__
-/* 
-Avoid this error:
-
-.../boost/math/special_functions/round.hpp:118:12: warning: implicit conversion from 'std::__2::numeric_limits<long long>::type' (aka 'long long') to 'float' changes value from 9223372036854775807 to 9223372036854775808 [-Wimplicit-int-float-conversion]
-.../boost/math/special_functions/round.hpp:125:11: note: in instantiation of function template specialization 'boost::math::llround<float, boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy> >' requested here
-.../orthanc/Core/JobsEngine/JobInfo.cpp:69:44: note: in instantiation of function template specialization 'boost::math::llround<float>' requested here
-
-.../boost/math/special_functions/round.hpp:86:12: warning: implicit conversion from 'std::__2::numeric_limits<int>::type' (aka 'int') to 'float' changes value from 2147483647 to 2147483648 [-Wimplicit-int-float-conversion]
-.../boost/math/special_functions/round.hpp:93:11: note: in instantiation of function template specialization 'boost::math::iround<float, boost::math::policies::policy<boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy, boost::math::policies::default_policy> >' requested here
-.../orthanc/Core/JobsEngine/JobInfo.cpp:133:39: note: in instantiation of function template specialization 'boost::math::iround<float>' requested here
-*/
-#pragma GCC diagnostic ignored "-Wimplicit-int-float-conversion"
-#endif 
-
 #include "JobInfo.h"
 
+#include "../CompatibilityMath.h"
 #include "../OrthancException.h"
 
-// This "include" is mandatory for Release builds using Linux Standard Base
-#include <boost/math/special_functions/round.hpp>
 
 namespace Orthanc
 {
@@ -79,7 +63,7 @@
       if (progress > 0.01f &&
           ms > 0.01f)
       {
-        long long remaining = boost::math::llround(ms / progress * (1.0f - progress));
+        long long remaining = Math::llround(ms / progress * (1.0f - progress));
         eta_ = timestamp_ + boost::posix_time::milliseconds(remaining);
         hasEta_ = true;
       }
@@ -189,7 +173,7 @@
     target["Timestamp"] = boost::posix_time::to_iso_string(timestamp_);
     target["CreationTime"] = boost::posix_time::to_iso_string(creationTime_);
     target["EffectiveRuntime"] = static_cast<double>(runtime_.total_milliseconds()) / 1000.0;
-    target["Progress"] = boost::math::iround(status_.GetProgress() * 100.0f);
+    target["Progress"] = Math::iround(status_.GetProgress() * 100.0f);
     
     if (status_.HasUserData())
     {
--- a/OrthancFramework/Sources/MetricsRegistry.cpp	Tue Jan 27 13:15:50 2026 +0100
+++ b/OrthancFramework/Sources/MetricsRegistry.cpp	Tue Jan 27 16:07:48 2026 +0100
@@ -27,10 +27,10 @@
 
 #include "ChunkedBuffer.h"
 #include "Compatibility.h"
+#include "CompatibilityMath.h"
 #include "Logging.h"
 #include "OrthancException.h"
 
-#include <boost/math/special_functions/round.hpp>
 
 namespace Orthanc
 {
@@ -339,7 +339,7 @@
     
     virtual void UpdateFloat(float value) ORTHANC_OVERRIDE
     {
-      value_.Update(boost::math::llround(value), GetPolicy());
+      value_.Update(Orthanc::Math::llround(value), GetPolicy());
     }
 
     virtual void UpdateInteger(int64_t value) ORTHANC_OVERRIDE
--- a/OrthancFramework/Sources/RestApi/RestApi.cpp	Tue Jan 27 13:15:50 2026 +0100
+++ b/OrthancFramework/Sources/RestApi/RestApi.cpp	Tue Jan 27 16:07:48 2026 +0100
@@ -25,12 +25,12 @@
 #include "../PrecompiledHeaders.h"
 #include "RestApi.h"
 
+#include "../CompatibilityMath.h"
 #include "../HttpServer/StringHttpOutput.h"
 #include "../Logging.h"
 #include "../OrthancException.h"
 
 #include <boost/algorithm/string/replace.hpp>
-#include <boost/math/special_functions/round.hpp>
 #include <stdlib.h>   // To define "_exit()" under Windows
 #include <stdio.h>
 
@@ -356,7 +356,7 @@
     
         LOG(WARNING) << "The documentation of the REST API contains " << GetSuccessPathsCount()
                      << " paths over a total of " << GetTotalPathsCount() << " paths "
-                     << "(coverage: " << static_cast<unsigned int>(boost::math::iround(coverage)) << "%)";
+                     << "(coverage: " << static_cast<unsigned int>(Math::iround(coverage)) << "%)";
       }
     };
 
--- a/OrthancFramework/UnitTestsSources/FrameworkTests.cpp	Tue Jan 27 13:15:50 2026 +0100
+++ b/OrthancFramework/UnitTestsSources/FrameworkTests.cpp	Tue Jan 27 16:07:48 2026 +0100
@@ -35,6 +35,7 @@
 
 #include <gtest/gtest.h>
 
+#include "../Sources/CompatibilityMath.h"
 #include "../Sources/DicomFormat/DicomTag.h"
 #include "../Sources/HttpServer/HttpToolbox.h"
 #include "../Sources/Logging.h"
@@ -51,6 +52,7 @@
 #endif
 
 #include <ctype.h>
+#include <limits>
 
 
 using namespace Orthanc;
@@ -1855,3 +1857,46 @@
   ASSERT_TRUE(b.GetContent().isString());
   ASSERT_EQ("Hello", b.GetContent().asString());
 }
+
+
+TEST(Math, Rounding)
+{
+  ASSERT_EQ(42, Math::iround(42.005f));
+  ASSERT_EQ(42, Math::iround(41.995f));
+  ASSERT_EQ(-42, Math::iround(-42.005f));
+  ASSERT_EQ(-42, Math::iround(-41.995f));
+
+  ASSERT_EQ(43, Math::iround(43.005));
+  ASSERT_EQ(43, Math::iround(42.995));
+  ASSERT_EQ(-43, Math::iround(-43.005));
+  ASSERT_EQ(-43, Math::iround(-42.995));
+
+  ASSERT_EQ(44ll, Math::llround(44.005f));
+  ASSERT_EQ(44ll, Math::llround(43.995f));
+  ASSERT_EQ(-44ll, Math::llround(-44.005f));
+  ASSERT_EQ(-44ll, Math::llround(-43.995f));
+
+  ASSERT_EQ(45ll, Math::llround(45.005));
+  ASSERT_EQ(45ll, Math::llround(44.995));
+  ASSERT_EQ(-45ll, Math::llround(-45.005));
+  ASSERT_EQ(-45ll, Math::llround(-44.995));
+
+  long long a;
+  ASSERT_TRUE(Math::llround(a, 46.005f));  ASSERT_EQ(46ll, a);
+  ASSERT_TRUE(Math::llround(a, 45.995f));  ASSERT_EQ(46ll, a);
+  ASSERT_TRUE(Math::llround(a, -46.005f));  ASSERT_EQ(-46ll, a);
+  ASSERT_TRUE(Math::llround(a, -45.995f));  ASSERT_EQ(-46ll, a);
+
+  ASSERT_FALSE(Math::llround(a, std::numeric_limits<float>::quiet_NaN()));
+  ASSERT_FALSE(Math::llround(a, std::numeric_limits<float>::infinity()));
+  ASSERT_FALSE(Math::llround(a, 1e20f /* float too large */));
+
+  ASSERT_TRUE(Math::llround(a, 47.005));  ASSERT_EQ(47ll, a);
+  ASSERT_TRUE(Math::llround(a, 46.995));  ASSERT_EQ(47ll, a);
+  ASSERT_TRUE(Math::llround(a, -47.005));  ASSERT_EQ(-47ll, a);
+  ASSERT_TRUE(Math::llround(a, -46.995));  ASSERT_EQ(-47ll, a);
+
+  ASSERT_FALSE(Math::llround(a, std::numeric_limits<double>::quiet_NaN()));
+  ASSERT_FALSE(Math::llround(a, std::numeric_limits<double>::infinity()));
+  ASSERT_FALSE(Math::llround(a, 1e20 /* double too large */));
+}
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Tue Jan 27 13:15:50 2026 +0100
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Tue Jan 27 16:07:48 2026 +0100
@@ -69,7 +69,6 @@
 #include "PluginsEnumerations.h"
 #include "PluginsJob.h"
 
-#include <boost/math/special_functions/round.hpp>
 #include <boost/regex.hpp>
 #include <dcmtk/dcmdata/dcdicent.h>
 #include <dcmtk/dcmnet/dimse.h>
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Tue Jan 27 13:15:50 2026 +0100
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp	Tue Jan 27 16:07:48 2026 +0100
@@ -38,6 +38,7 @@
 #include "../../../OrthancFramework/Sources/Logging.h"
 #include "../../../OrthancFramework/Sources/MultiThreading/Semaphore.h"
 #include "../../../OrthancFramework/Sources/SerializationToolbox.h"
+#include "../../../OrthancFramework/Sources/CompatibilityMath.h"
 
 #include "../OrthancConfiguration.h"
 #include "../Search/DatabaseLookup.h"
@@ -47,7 +48,6 @@
 #include "../SliceOrdering.h"
 
 // This "include" is mandatory for Release builds using Linux Standard Base
-#include <boost/math/special_functions/round.hpp>
 #include <boost/shared_ptr.hpp>
 
 #include "../../../OrthancFramework/Sources/FileStorage/StorageAccessor.h"
@@ -1170,8 +1170,8 @@
             ratio = static_cast<float>(argHeight) / static_cast<float>(decoded->GetHeight());
           }
           
-          targetWidth = boost::math::iround(ratio * static_cast<float>(decoded->GetWidth()));
-          targetHeight = boost::math::iround(ratio * static_cast<float>(decoded->GetHeight()));
+          targetWidth = Math::iround(ratio * static_cast<float>(decoded->GetWidth()));
+          targetHeight = Math::iround(ratio * static_cast<float>(decoded->GetHeight()));
         }
         
         if (decoded->GetFormat() == PixelFormat_RGB24 || decoded->GetFormat() == PixelFormat_RGB48)