如何在不调用未定义的行为的情况下检查一个值适合类型?

发布于 2025-01-18 08:58:56 字数 1079 浏览 4 评论 0原文

我希望检查double值是否可以表示为int(或对于整数类型的任何一对浮点)。这是一种简单的方法:

double x = ...;
int i = x; // potentially undefined behaviour

if ((double) i != x) {
    // not representable
}

但是,它在标记的线上调用了未定义的行为,并触发了瑞银(有些人会抱怨)。

问题:

  • 这种方法一般认为可以接受吗?
  • 在不调用未定义行为的情况下,是否有合理简单的方法可以做到这一点?

按要求进行澄清:

我现在面临的情况涉及从double转换为各种整数类型(intlonglong>但是。整数和整数 - >浮点转换。

转换如何失败的示例:

  • float->整数转换可能失败是值不是整数,例如3.5
  • 源值可以超出目标类型的范围(比最大和最小代表值更大或小)。例如1.23E100
  • 源值可能为 +-Inf或Nan,NAN很棘手,因为与其任何比较返回false。
  • 整数 - >当浮点类型没有足够的精度时,浮点转换可能会失败。例如,典型的double具有52个二进制数字,而64位整数类型中有63位数字。例如,在典型的64位系统上,(long)(double)((1l<<< 53) + 1L)
  • 我知道1L<< 53(而不是(1L<< 53) + 1)在技术上完全可以表示为double,并且我提出的代码会接受这种转换,即使可能不允许。
  • 我没想到什么?

I am looking to check if a double value can be represented as an int (or the same for any pair of floating point an integer types). This is a simple way to do it:

double x = ...;
int i = x; // potentially undefined behaviour

if ((double) i != x) {
    // not representable
}

However, it invokes undefined behaviour on the marked line, and triggers UBSan (which some will complain about).

Questions:

  • Is this method considered acceptable in general?
  • Is there a reasonably simple way to do it without invoking undefined behaviour?

Clarifications, as requested:

The situation I am facing right now involves conversion from double to various integer types (int, long, long long) in C. However, I have encountered similar situations before, thus I am interested in answers both for float -> integer and integer -> float conversions.

Examples of how the conversion may fail:

  • Float -> integer conversion may fail is the value is not a whole number, e.g. 3.5.
  • The source value may be out of the range of the target type (larger or small than max and min representable values). For example 1.23e100.
  • The source values may be +-Inf or NaN, NaN being tricky as any comparison with it returns false.
  • Integer -> float conversion may fail when the float type does not have enough precision. For example, typical double have 52 binary digits compared to 63 digits in a 64-bit integer type. For example, on a typical 64-bit system, (long) (double) ((1L << 53) + 1L).
  • I do understand that 1L << 53 (as opposed to (1L << 53) + 1) is technically exactly representable as a double, and that the code I proposed would accept this conversion, even though it probably shouldn't be allowed.
  • Anything I didn't think of?

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

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

发布评论

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

评论(3

白云悠悠 2025-01-25 08:58:56

创建范围限制与FP类型

“窍门”是在不失去精度的情况下形成限制。

让我们考虑float int

float转换为int是有效的(例如,32位2的补语int),以-2,147,483,648.9999 ...至2,147,483,647.99999 ...或几乎 int_min -1 to int_max + 1。

我们可以利用integer_max始终是一种力量-2-1和integer_min is-(2)(用于常见2的补语)。

避免fp_int_min_minus_1的限制,因为它可能/可能无法完全编码为FP。

// Form FP limits of "INT_MAX plus 1" and "INT_MIN"
#define FLOAT_INT_MAX_P1 ((INT_MAX/2 + 1)*2.0f)
#define FLOAT_INT_MIN ((float) INT_MIN)

if (f < FLOAT_INT_MAX_P1 && f - FLOAT_INT_MIN > -1.0f) {
  // Within range.
  
  Use modff() to detect a fraction if desired.
}

更多的pedantic代码将使用!isnan(f),并考虑Non-2的补充编码。

Create range limits exactly as FP types

The "trick" is to form the limits without loosing precision.

Let us consider float to int.

Conversion of float to int is valid (for example with 32-bit 2's complement int) for -2,147,483,648.9999... to 2,147,483,647.9999... or nearly INT_MIN -1 to INT_MAX + 1.

We can take advantage that integer_MAX is always a power-of-2 - 1 and integer_MIN is -(power-of-2) (for common 2's complement).

Avoid the limit of FP_INT_MIN_minus_1 as it may/may not be exactly encodable as a FP.

// Form FP limits of "INT_MAX plus 1" and "INT_MIN"
#define FLOAT_INT_MAX_P1 ((INT_MAX/2 + 1)*2.0f)
#define FLOAT_INT_MIN ((float) INT_MIN)

if (f < FLOAT_INT_MAX_P1 && f - FLOAT_INT_MIN > -1.0f) {
  // Within range.
  
  Use modff() to detect a fraction if desired.
}

More pedantic code would use !isnan(f) and consider non-2's complement encoding.

芸娘子的小脾气 2025-01-25 08:58:56

使用已知限制和浮点数有效性。检查 limits.h 标头中的内容。

您可以编写如下内容:

#include <limits.h>
#include <math.h> 

// Of course, constants used are specific to "int" type... There is others for other types.
if ((isnormal(x)) && (x>=INT_MIN) && (x<=INT_MAX) && (round(x)==x))
  // Safe assignation from double to int.
  i = (int)x ;
else
  // Handle error/overflow here.
  ERROR(.....) ;

显然,代码依赖于惰性布尔求值。

Using known limits and floating-point number validity. Check what's inside limits.h header.

You can write something like this:

#include <limits.h>
#include <math.h> 

// Of course, constants used are specific to "int" type... There is others for other types.
if ((isnormal(x)) && (x>=INT_MIN) && (x<=INT_MAX) && (round(x)==x))
  // Safe assignation from double to int.
  i = (int)x ;
else
  // Handle error/overflow here.
  ERROR(.....) ;

Code relies on lazy boolean evaluation, obviously.

情绪操控生活 2025-01-25 08:58:56

请参考 IEEE 754 内存中浮点数的表示
https://en.wikipedia.org/wiki/IEEE_754

以 double 为例:

  • Sign位:1 位
  • 指数:11 位
  • 小数:52 位

这里需要指出三个特殊值:

  1. 如果指数为 0 并且尾数的小数部分为 0,则该数字为±0
  2. 如果指数为 2047 并且尾数的小数部分为 0,则该数字为 ±∞
  3. 如果指数为 2047 并且尾数的小数部分非零,则该数字为 NaN。

这是64位上double转int的例子,仅供参考

#include <stdint.h>

#define EXPBITS      11
#define FRACTIONBITS 52
#define GENMASK(n)   (((uint64_t)1 << (n)) - 1)
#define EXPBIAS      GENMASK(EXPBITS - 1) 
#define SIGNMASK     (~GENMASK(FRACTIONBITS + EXPBITS)) 
#define EXPMASK      (GENMASK(EXPBITS) << FRACTIONBITS) 
#define FRACTIONMASK GENMASK(FRACTIONBITS) 

int double_to_int(double src, int *dst)
{
    union {
        double d;
        uint64_t i;
    } y;

    int exp;
    int sign;
    int maxbits;
    uint64_t fraction;

    y.d = src;
    sign = (y.i & SIGNMASK) ? 1 : 0;
    exp = (y.i & EXPMASK) >> FRACTIONBITS;
    fraction = (y.i & FRACTIONMASK);

    // 0
    if (fraction == 0 && exp == 0) {
        *dst = 0;
        return 0;
    }

    exp -= EXPBIAS;
    // not a whole number
    if (exp < 0)
        return -1;

    // out of the range of int
    maxbits = sizeof(*dst) * 8 - 1;
    if (exp >= maxbits && !(exp == maxbits && sign && fraction == 0))
        return -2;

    // not a whole number
    if (fraction & GENMASK(FRACTIONBITS - exp))
        return -3;

    // convert to int
    *dst = src;

    return 0;
}

Please refer to IEEE 754 representation of floating point numbers in Memory
https://en.wikipedia.org/wiki/IEEE_754

Take double as an example:

  • Sign bit: 1 bit
  • Exponent: 11 bits
  • Fraction: 52 bits

There are three special values to point out here:

  1. If the exponent is 0 and the fractional part of the mantissa is 0, the number is ±0
  2. If the exponent is 2047 and the fractional part of the mantissa is 0, the number is ±∞
  3. If the exponent is 2047 and the fractional part of the mantissa is non-zero, the number is NaN.

This is an example of convert from double to int on 64-bit, just for reference

#include <stdint.h>

#define EXPBITS      11
#define FRACTIONBITS 52
#define GENMASK(n)   (((uint64_t)1 << (n)) - 1)
#define EXPBIAS      GENMASK(EXPBITS - 1) 
#define SIGNMASK     (~GENMASK(FRACTIONBITS + EXPBITS)) 
#define EXPMASK      (GENMASK(EXPBITS) << FRACTIONBITS) 
#define FRACTIONMASK GENMASK(FRACTIONBITS) 

int double_to_int(double src, int *dst)
{
    union {
        double d;
        uint64_t i;
    } y;

    int exp;
    int sign;
    int maxbits;
    uint64_t fraction;

    y.d = src;
    sign = (y.i & SIGNMASK) ? 1 : 0;
    exp = (y.i & EXPMASK) >> FRACTIONBITS;
    fraction = (y.i & FRACTIONMASK);

    // 0
    if (fraction == 0 && exp == 0) {
        *dst = 0;
        return 0;
    }

    exp -= EXPBIAS;
    // not a whole number
    if (exp < 0)
        return -1;

    // out of the range of int
    maxbits = sizeof(*dst) * 8 - 1;
    if (exp >= maxbits && !(exp == maxbits && sign && fraction == 0))
        return -2;

    // not a whole number
    if (fraction & GENMASK(FRACTIONBITS - exp))
        return -3;

    // convert to int
    *dst = src;

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