C中的数组索引,它必须是整数吗?可以是浮点数吗?

发布于 2024-10-15 03:37:34 字数 465 浏览 1 评论 0原文

正如标题所示...我需要使用浮点作为数组索引,但 GCC 编译器吐出一个错误抱怨。

基本上我有一个数学函数 F(x,t),其中该函数具有变量 x 和 t。我遇到的问题是我试图增加 float 类型中的 x 和 t,以便我可以在不同的 x 和 t 处计算函数的不同值。所以很自然地我会有两个 for 循环:

for (x = x_min; x < x_max; x += dx) {
    for (t = t_min; t < t_min; t += dt) {
        f[x][t] = 10*x + 10*t; // Over simplified.. but you get the idea
    }
}

// And then perform some fourier transform
fft(f[x][t], 256, 1);

所以是的,这就是为什么我想知道是否可以获取浮点作为数组索引。

as the title suggests... I need to use floating points as array indexes, but the GCC compiler spits out an error complaining.

Basically I have a mathematical function say F(x,t) where the function has variables x and t. The problem I'm having is that I'm trying to increment x and t in type float, so that I can calculate different values for the function at different x, and t. So naturally I would have two for loops:

for (x = x_min; x < x_max; x += dx) {
    for (t = t_min; t < t_min; t += dt) {
        f[x][t] = 10*x + 10*t; // Over simplified.. but you get the idea
    }
}

// And then perform some fourier transform
fft(f[x][t], 256, 1);

So yea, that is why I was wondering if it is possible to get floating points as the array index.

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

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

发布评论

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

评论(7

心作怪 2024-10-22 03:37:34

是的,它必须是一个整数,因为您本质上是在执行指针算术,其中 &array[0] 是指向数组开头的指针(嗯,从技术上讲,它必须是< /em> 一个整数,因为规范就是这么说的,但这就是原因)。

在这种情况下,从基指针向上移动对象大小的一部分是没有意义的。您几乎可以保证不会指向元素的开头。

这样看:

int array[10] = { 0 };

// analagous to *(array + 5), where '5' is 
// offsetting the pointer by sizeof(int) * 5 bytes   
// If you were able to add 5.5 to the base address 
// the value assigned to 'i' below would be interpreted as
// the four bytes following *(array + 5.5), i.e., garbage data.
int i = array[5];  

既然我一开始就觉得这是一个奇怪的问题,也许您可​​以向我们提供更多有关您实际想要完成的任务的信息,而不是您提出的解决方案?在这种情况下,我们或许可以为您提供更有帮助的答案。

Yes, it has to be an integer because you are essentially performing pointer arithmetic where &array[0] is a pointer to the beginning of the array (well, technically it has to be an integer because that's what the spec says, but this is why).

In that context it makes no sense to move from the base pointer up some fraction of the size of an object. You are pretty much guaranteeing that you won't be pointing to the beginning of an element.

Look at it this way:

int array[10] = { 0 };

// analagous to *(array + 5), where '5' is 
// offsetting the pointer by sizeof(int) * 5 bytes   
// If you were able to add 5.5 to the base address 
// the value assigned to 'i' below would be interpreted as
// the four bytes following *(array + 5.5), i.e., garbage data.
int i = array[5];  

Since this strikes me as an odd question to begin with, perhaps you could give us more information regarding what you are actually trying to accomplish rather than your proposed solution? We can probably give you more helpful answers in that case.

半仙 2024-10-22 03:37:34

如果您只是将整数存储在浮点变量中,则将值转换或以其他方式转换为整数类型应该可以正常工作。例如:

array[(int)x] = y;

如果你真的想用非整数索引来索引一个数组,你将不得不自己设计一个更高级别的数据结构,并且它可能不会是一个时间意义上的“数组” -效率特性。

If you're just storing whole numbers in floating point variables, casting or otherwise converting the values to an integer type should work just fine. For instance:

array[(int)x] = y;

If you really want to index an array with non-integral indices, you're going to have to design yourself a higher-level data structure, and it will probably not be an "array" in the sense of its time-efficiency properties.

旧伤还要旧人安 2024-10-22 03:37:34

是的。来自 C99 标准 §6.5.2.1(数组下标):

其中一个表达式的类型应为“指向对象type”的指针,另一个表达式应为整数类型,结果的类型为“type” '.

如果要使用浮点数作为数组索引,则需要将其转换为整数。这通常是一个坏主意,因为计算过程中任何轻微的舍入错误都可能很容易导致数组索引在截断后偏移 1。

Yes. From the C99 standard §6.5.2.1 (Array Subscripting):

One of the expressions shall have type ‘‘pointer to object type’’, the other expression shall have integer type, and the result has type ‘‘type’’.

If you want to use a floating-point number as an array index, you need to cast it to an integer. This is often a bad idea, because any slight rounding errors during your calculations could easily result in the array index being off by 1 after the truncation.

勿忘初心 2024-10-22 03:37:34

正如您所发现的,数组索引必须是整型。为了达到您想要的效果,您可以按浮点增量缩放和偏移整数索引:

double x, t;
int x_i, t_i;

for (x_i = 0; x_i < NX; x_i ++) {
    x = x_min + x_i * dx;
    for (t_i = 0; t_i < NT, t_i++) {
        t = t_min + t_i * dt;
        f[x_i][t_i] = 10*x + 10*t;
    }
}

As you have discovered, the array indices must be integral types. To achieve the effect you want, you can scale and offset the integer index by the floating point deltas:

double x, t;
int x_i, t_i;

for (x_i = 0; x_i < NX; x_i ++) {
    x = x_min + x_i * dx;
    for (t_i = 0; t_i < NT, t_i++) {
        t = t_min + t_i * dt;
        f[x_i][t_i] = 10*x + 10*t;
    }
}
娇纵 2024-10-22 03:37:34

C 中的数组索引必须是整数

Array indices in C must be integral

美人如玉 2024-10-22 03:37:34

根据您真正想要执行的操作,您可能可以使用浮点数的缩放偏移版本作为“索引”:

#define arraySize 100
Entry array[arraySize];
float scaleFactor = 10;
float base = 0.1;
float value = 0.3;
// This truncation is where we have a many to one mapping.
index = (int)( (value - base) * scaleFactor);
if (index >=0 && index < arraySize)
    Entry* entry = array + index;

Depending on what it is you really want to do, you might be able to use a scaled an offset version of your float as an "index":

#define arraySize 100
Entry array[arraySize];
float scaleFactor = 10;
float base = 0.1;
float value = 0.3;
// This truncation is where we have a many to one mapping.
index = (int)( (value - base) * scaleFactor);
if (index >=0 && index < arraySize)
    Entry* entry = array + index;
巡山小妖精 2024-10-22 03:37:34

是的,我们可以,但是每个存储字节将花费 64 KiByte 内存,

浮点数存储在 4 个字节 = 32 位中,
您可以使用整数指针“读取”它。
不转换/转换/舍入为整数,而是“读取”原始字节。
在c代码中:

float f = 3.1415;

int *ip = NULL;
ip = (int *)&f;

printf("f = %f\ni = %i = 0x%x\n", f, *ip, *ip);

// f = 3.141500
// i = 1078529622 = 0x40490e56
//                    ^^^^
//                    bfloat16

使用所有浮点数作为数组索引,
我们需要 2**32 Byte = 4 GiByte 的内存

来减少内存使用,我们可以使用 bfloat16 类型,
这只是一个“截断”版本(有损压缩)
32 位浮点数,
只有 7 位尾数:

bfloat16 = SEEEEEEE EMMMMMMM
 float32 = SEEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM

bfloat16 开始大肆宣传“机器学习”,
但也用于传感器数据。
一些奇特的处理器具有对 bfloat16 的硬件支持。

float32 和 bfloat16 之间的转换很简单,
只是由于不同的字节顺序(字节顺序)而变得复杂,

bfloat16.c

// convert between float32 and bfloat16
// use float as array index --> see end of file
// public domain + no warranty

#include <stdint.h> // uint16_t, int8_t
#include <stdlib.h> // malloc, free, size_t
#include <string.h> // memset
#include <stdbool.h> // bool
#include <stdio.h> // printf



typedef uint16_t bfloat16; // --> "maybe remove"
typedef float     float32;



// stolen from tensorflow bfloat16.cc

void float32_to_bfloat16_be(float32 *src, bfloat16 *dst, size_t size)
{
    uint16_t *s = (uint16_t *)src;
    uint16_t *d = (uint16_t *)dst; // maybe remove

    for (; size != 0; s += 2, d++, size--) {
        // big endian byte order
        *d = s[0];
    }
}

void float32_to_bfloat16_le(float32 *src, bfloat16 *dst, size_t size)
{
    uint16_t *s = (uint16_t *)src;
    uint16_t *d = (uint16_t *)dst; // maybe remove

    for (; size != 0; s += 2, d++, size--) {
        // little endian byte order
        *d = s[1];
    }
}

void bfloat16_to_float32_be(bfloat16* src, float32* dst, size_t size)
{
    uint16_t *s = (uint16_t *)src; // maybe remove
    uint16_t *d = (uint16_t *)dst;

    for (; size != 0; s++, d += 2, size--) {
        // big endian byte order
        d[0] = *s;
        d[1] = 0;
    }
}

void bfloat16_to_float32_le(bfloat16* src, float32* dst, size_t size)
{
    uint16_t *s = (uint16_t *)src; // maybe remove
    uint16_t *d = (uint16_t *)dst;

    for (; size != 0; s++, d += 2, size--) {
        // little endian byte order
        d[0] = 0;
        d[1] = *s;
    }
}



// detect byte order at runtime
// http://esr.ibiblio.org/?p=5095#comment-415728
// this usually does not generate any code at all with GCC even with -O1
// ignore byte orders other than LE and BE

static inline bool is_big_endian() {
    const uint16_t endianness = 256; // 0b 00000001 00000000
    return *(const uint8_t *)&endianness;
}



// demo program

int main()
{
    // function pointers for the current byte order
    void (*float32_to_bfloat16)(float32*, bfloat16*, size_t) = NULL;
    void (*bfloat16_to_float32)(bfloat16*, float32*, size_t) = NULL;

    // detect byte order at runtime
    if (is_big_endian())
    {
        printf("byte order is big endian\n");

        float32_to_bfloat16 = &float32_to_bfloat16_be;
        bfloat16_to_float32 = &bfloat16_to_float32_be;
    }
    else
    {
        printf("byte order is little endian\n");

        float32_to_bfloat16 = &float32_to_bfloat16_le;
        bfloat16_to_float32 = &bfloat16_to_float32_le;
    }



    // convert one number

     float32 a1 = 3.1415;
    bfloat16 b1 = 0;

    float32_to_bfloat16(&a1, &b1, 1);



    bfloat16 a2 = 0x4049;
     float32 b2 = 0;

    bfloat16_to_float32(&a2, &b2, 1);



    printf("%1.4f --> 0x%04x\n", a1, b1);
    printf("%1.4f <-- 0x%04x\n", b2, a2);



    // convert many numbers

     float32 a3[2] = {2.7182, 1.4142};
    bfloat16 b3[2] = {0};

    float32_to_bfloat16(a3, b3, 2);



    bfloat16 a4[2] = {0x402d, 0x3fb5};
     float32 b4[2] = {0};

    bfloat16_to_float32(a4, b4, 2);



    printf("%1.4f --> 0x%04x\n", a3[0], b3[0]);
    printf("%1.4f <-- 0x%04x\n", b4[0], a4[0]);

    printf("%1.4f --> 0x%04x\n", a3[1], b3[1]);
    printf("%1.4f <-- 0x%04x\n", b4[1], a4[1]);



    // array with float index [half-float index]
    int8_t *n_fi = NULL; // int8_t = -128 .... +127

    // init array
    n_fi = malloc(0xffff * sizeof(typeof(*n_fi)));
    memset(n_fi, 0, 0xffff * sizeof(typeof(*n_fi)));
    // 0xffff = 2**16-1 = 65535 = 64Ki-1

    // key
    float32 k = 3.1415;

    // convert key
    bfloat16 k16 = 0;
    float32_to_bfloat16(&k, &k16, 1);

    // value
    int8_t v = 123;

    // write
    n_fi[k16] = v;

    // read
    printf("n_fi[0x%04x] = %i\n", k16, n_fi[k16]);

    // read next = zero from array init
    printf("n_fi[0x%04x] = %i\n", k16+1, n_fi[k16+1]);

    // close array
    free(n_fi);

}

每个字节 64 KiByte 太多了吗?
我们可以将键空间减少为“小的正浮点数”
但我们需要边界检查来防止段错误。

yes we can, but it will cost 64 KiByte memory per stored byte

a float is stored in 4 bytes = 32 bits,
which you can "read" with an integer pointer.
not convert/cast/round to integer, but "read" the raw bytes.
in c code:

float f = 3.1415;

int *ip = NULL;
ip = (int *)&f;

printf("f = %f\ni = %i = 0x%x\n", f, *ip, *ip);

// f = 3.141500
// i = 1078529622 = 0x40490e56
//                    ^^^^
//                    bfloat16

to use all floats as array index,
we would need 2**32 Byte = 4 GiByte of memory

to reduce memory use, we can use the bfloat16 type,
which is simply a "truncated" version (lossy compression)
of the 32 bit float,
with only 7 bit mantissa:

bfloat16 = SEEEEEEE EMMMMMMM
 float32 = SEEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM

bfloat16 came to hype with "machine learning",
but is also used for sensor data.
some exotic processors have hardware support for bfloat16.

the conversion between float32 and bfloat16 is trivial,
only complicated by different byte order (endianness)

bfloat16.c

// convert between float32 and bfloat16
// use float as array index --> see end of file
// public domain + no warranty

#include <stdint.h> // uint16_t, int8_t
#include <stdlib.h> // malloc, free, size_t
#include <string.h> // memset
#include <stdbool.h> // bool
#include <stdio.h> // printf



typedef uint16_t bfloat16; // --> "maybe remove"
typedef float     float32;



// stolen from tensorflow bfloat16.cc

void float32_to_bfloat16_be(float32 *src, bfloat16 *dst, size_t size)
{
    uint16_t *s = (uint16_t *)src;
    uint16_t *d = (uint16_t *)dst; // maybe remove

    for (; size != 0; s += 2, d++, size--) {
        // big endian byte order
        *d = s[0];
    }
}

void float32_to_bfloat16_le(float32 *src, bfloat16 *dst, size_t size)
{
    uint16_t *s = (uint16_t *)src;
    uint16_t *d = (uint16_t *)dst; // maybe remove

    for (; size != 0; s += 2, d++, size--) {
        // little endian byte order
        *d = s[1];
    }
}

void bfloat16_to_float32_be(bfloat16* src, float32* dst, size_t size)
{
    uint16_t *s = (uint16_t *)src; // maybe remove
    uint16_t *d = (uint16_t *)dst;

    for (; size != 0; s++, d += 2, size--) {
        // big endian byte order
        d[0] = *s;
        d[1] = 0;
    }
}

void bfloat16_to_float32_le(bfloat16* src, float32* dst, size_t size)
{
    uint16_t *s = (uint16_t *)src; // maybe remove
    uint16_t *d = (uint16_t *)dst;

    for (; size != 0; s++, d += 2, size--) {
        // little endian byte order
        d[0] = 0;
        d[1] = *s;
    }
}



// detect byte order at runtime
// http://esr.ibiblio.org/?p=5095#comment-415728
// this usually does not generate any code at all with GCC even with -O1
// ignore byte orders other than LE and BE

static inline bool is_big_endian() {
    const uint16_t endianness = 256; // 0b 00000001 00000000
    return *(const uint8_t *)&endianness;
}



// demo program

int main()
{
    // function pointers for the current byte order
    void (*float32_to_bfloat16)(float32*, bfloat16*, size_t) = NULL;
    void (*bfloat16_to_float32)(bfloat16*, float32*, size_t) = NULL;

    // detect byte order at runtime
    if (is_big_endian())
    {
        printf("byte order is big endian\n");

        float32_to_bfloat16 = &float32_to_bfloat16_be;
        bfloat16_to_float32 = &bfloat16_to_float32_be;
    }
    else
    {
        printf("byte order is little endian\n");

        float32_to_bfloat16 = &float32_to_bfloat16_le;
        bfloat16_to_float32 = &bfloat16_to_float32_le;
    }



    // convert one number

     float32 a1 = 3.1415;
    bfloat16 b1 = 0;

    float32_to_bfloat16(&a1, &b1, 1);



    bfloat16 a2 = 0x4049;
     float32 b2 = 0;

    bfloat16_to_float32(&a2, &b2, 1);



    printf("%1.4f --> 0x%04x\n", a1, b1);
    printf("%1.4f <-- 0x%04x\n", b2, a2);



    // convert many numbers

     float32 a3[2] = {2.7182, 1.4142};
    bfloat16 b3[2] = {0};

    float32_to_bfloat16(a3, b3, 2);



    bfloat16 a4[2] = {0x402d, 0x3fb5};
     float32 b4[2] = {0};

    bfloat16_to_float32(a4, b4, 2);



    printf("%1.4f --> 0x%04x\n", a3[0], b3[0]);
    printf("%1.4f <-- 0x%04x\n", b4[0], a4[0]);

    printf("%1.4f --> 0x%04x\n", a3[1], b3[1]);
    printf("%1.4f <-- 0x%04x\n", b4[1], a4[1]);



    // array with float index [half-float index]
    int8_t *n_fi = NULL; // int8_t = -128 .... +127

    // init array
    n_fi = malloc(0xffff * sizeof(typeof(*n_fi)));
    memset(n_fi, 0, 0xffff * sizeof(typeof(*n_fi)));
    // 0xffff = 2**16-1 = 65535 = 64Ki-1

    // key
    float32 k = 3.1415;

    // convert key
    bfloat16 k16 = 0;
    float32_to_bfloat16(&k, &k16, 1);

    // value
    int8_t v = 123;

    // write
    n_fi[k16] = v;

    // read
    printf("n_fi[0x%04x] = %i\n", k16, n_fi[k16]);

    // read next = zero from array init
    printf("n_fi[0x%04x] = %i\n", k16+1, n_fi[k16+1]);

    // close array
    free(n_fi);

}

64 KiByte per Byte is too much?
we can reduce the key space to "small positive floats"
but then we need boundary checks to prevent segfaults.

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