编译时间检查LCM(a,b)是否没有溢出

发布于 2025-02-04 05:24:53 字数 1963 浏览 1 评论 0原文

我想进行一项编译时间检查,即需要两个数字中最不常见的倍数不会溢出。难度:关于std :: LCM

如果| m |,| n |或最不常见的倍数,则行为是不确定的 | M |和| n |不能表示类型的值 std :: common_type_t< m,n>。

source https://en.cppreference.com/ w/cpp/numeric/lcm

这是我想到的:

#include <type_traits>
#include <cstdint>
#include <numeric>

template<int8_t a, int8_t b,
  std::enable_if_t<
    (std::lcm(a,b) > 0 && (std::lcm(a,b) % a == 0) && (std::lcm(a,b) % b == 0)),
    std::nullptr_t> = nullptr>
void f() { }

这里的理由是,条件检查std :: lcm(a,b)已经产生了一个类型std :: common_type_t&lt; typeof(a),typeof(b)&gt;a ab 。鉴于 ab的某些常见倍数适合std :: common_type_t&lt; typeof(a),typeof(b)&gt ;,请参见 最小值 common多拟合,因此,我们可以通过定义std :: lcm来保证我们所计算的实际上是LCM。

我检查了这似乎正常工作,例如

f<3, 5>();      // compiles
f<127, 127>();  // compiles
f<35, 48>();    // doesn't compile

,我有几个问题。

  1. 该文档说,如果最小常见的倍数不可代表,行为是不确定的,而不仅仅是实现依赖性或其他东西。这是否意味着一个包含f&lt; 35,48&gt;()之类的程序是错误的,并且欢迎编译器实际上编译上述代码并进行任意结果?
  2. 是否有一种更简单的方法来做我要做的事情?

我想我可以编写自己的constexpr safe_lcm函数,该功能可以保证定义的行为并在溢出的情况下返回0,但这似乎是一个非常不高的解决方案,而且我也必须努力工作为了确保我涵盖了可能会喂养的算术类型的所有可能组合。

Update :听起来像是在恒定表达式中不允许使用未定义的行为。由于我显然需要这是一个恒定的表达方式才能在编译时使用它,这是否意味着我在这里很安全?

更新2 :这似乎是针对无定义的行为理论的绝对打击:

template<int n> struct S {};

template<int8_t a, int8_t b>
S<std::lcm(a, b)> g()
{
  return S<std::lcm(a,b)>();
}

int main(int, char **)
{
  g<35, 48>(); // compiles :'(
  return 0;
}

I would like to have a compile-time check that taking the least common multiple of two numbers doesn't overflow. Difficulty: regarding std::lcm,

The behavior is undefined if |m|, |n|, or the least common multiple of
|m| and |n| is not representable as a value of type
std::common_type_t<M, N>.

(Source: https://en.cppreference.com/w/cpp/numeric/lcm)

Here is what I have come up with:

#include <type_traits>
#include <cstdint>
#include <numeric>

template<int8_t a, int8_t b,
  std::enable_if_t<
    (std::lcm(a,b) > 0 && (std::lcm(a,b) % a == 0) && (std::lcm(a,b) % b == 0)),
    std::nullptr_t> = nullptr>
void f() { }

The rationale here is that that the condition checks that std::lcm(a,b) has produced a positive number of type std::common_type_t<typeof(a), typeof(b)> which is a multiple of both a and b. Given that some common multiple of a and b fits in std::common_type_t<typeof(a), typeof(b)>, it follows that the least common multiple fits, and therefore we are guaranteed by the definition of std::lcm that what we have computed is in fact the lcm.

I checked that this appears to work correctly, e.g.

f<3, 5>();      // compiles
f<127, 127>();  // compiles
f<35, 48>();    // doesn't compile

However I have a couple of questions.

  1. The documentation says that if the least common multiple is not representable, the behavior is undefined, and not just implementation-dependent or something. Does this mean that a program containing something like f<35,48>() is ill-formed and that the compiler is welcome to actually compile said code with arbitrary results?
  2. Is there a simpler way of doing what I'm trying to do?

I suppose I could write my own constexpr safe_lcm function that would guarantee defined behavior and return 0 in the case of an overflow, but that seems like a pretty inelegant solution and also I'd have to work pretty hard to make sure I covered every conceivable combination of arithmetic types I might feed to it.

Update: It sounds like undefined behavior isn't allowed in constant expressions. Since I clearly need this to be a constant expression in order to use it at compile time, does that mean I'm safe here?

Update 2: This appears to be a definite strike against the no-undefined-behavior-in-constexpr theory:

template<int n> struct S {};

template<int8_t a, int8_t b>
S<std::lcm(a, b)> g()
{
  return S<std::lcm(a,b)>();
}

int main(int, char **)
{
  g<35, 48>(); // compiles :'(
  return 0;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

岁月蹉跎了容颜 2025-02-11 05:24:53

假设A和B都是积极的,那么

a * b == lcm(a,b) * gcd(a,b)

您可以利用这种关系来利用自己的优势。计算GCD(a,b)不会溢出。然后,您可以检查

a / gcd(a,b) <= std::numeric_limits<CT>::max() / b

CT在哪里std :: common_type_t&lt; exptype(a),dectType(b)&gt;

Assuming that both a and b are positive, there is the relationship

a * b == lcm(a,b) * gcd(a,b)

that you can use to your advantage. Computing gcd(a,b) does not overflow. Then you can check whether

a / gcd(a,b) <= std::numeric_limits<CT>::max() / b

where CT is std::common_type_t<decltype(a),decltype(b)>.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文