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

时间:2011-02-02 00:47:32

标签: c arrays floating-point integer

正如标题所暗示的......我需要使用浮点数作为数组索引,但是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);

所以,这就是为什么我想知道是否有可能将浮点作为数组索引。

7 个答案:

答案 0 :(得分:5)

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

array[(int)x] = y;

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

答案 1 :(得分:4)

是的,它必须是一个整数,因为你实际上是在执行指针运算,其中&array[0]是一个指向数组开头的指针(从技术上来说,必须是一个整数因为那是规范所说的,但这就是原因)。

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

这样看:

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];  

因为这对我来说是一个奇怪的问题,或许你可以给我们更多关于你实际想要完成什么的信息而不是你提出的解决方案?在这种情况下,我们可能会给你更多有用的答案。

答案 2 :(得分:4)

是。来自C99标准§6.5.2.1(数组订阅):

  

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

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

答案 3 :(得分:2)

正如您所发现的,数组索引必须是整数类型。要获得所需的效果,可以通过浮点增量来缩放和偏移整数索引:

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;
    }
}

答案 4 :(得分:0)

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

答案 5 :(得分:0)

根据您真正想要做的事情,可能能够使用缩放版本的浮动版作为“索引”:

#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;

答案 6 :(得分:0)

是的,但是每个存储的字节将花费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字节= 4吉字节的内存

为减少内存使用,我们可以使用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是否太多?
我们可以将键空间减小到“小的正浮点数”
但是我们需要进行边界检查以防止段错误。