2009-07-13 3 views

ответ

2

Вы имеете в виду биты, установленные в представлении числа одиночной точности IEEE-754? Если это так, добавьте его в int (как float, так и int шириной 32 бит) и выполните обычный бит: SO question #109023.

+2

Кастинг в int будет усекать и конвертировать, а не давать вам необработанное представление. – bdonlan

+2

Хорошая точка. Таким образом, лучшим решением было бы объединение или плавание f; * (int *) & f –

+2

Я думаю, что Адриан имел в виду лить указатель, а не значение. –

4

Если вы хотите работать на фактическом побитовом представлении числа с плавающей точкой, вы должны сделать что-то вроде этого:

float f; /* whatever your float is */ 
int i = *(int *)&f; 

Что это делает взять адрес f с адресом-оператора, &. Этот адрес имеет тип float *, указатель на поплавок. Затем он обновляет его с помощью (int *), в котором говорится: «Притворитесь, что этот указатель больше не указывает на float, но теперь он указывает на int». Обратите внимание, что он не меняет значение на f. Затем последний * (или, во-первых, поскольку мы читаем справа налево) разыскивает этот указатель, который является указателем на int и поэтому возвращает int, a.k.a. целое число с тем же поразрядным представлением, что и float.

Чтобы сделать наоборот (конвертировать и int i обратно в float f), сделайте наоборот:

f = *(float *)&i; 

Если я не ошибаюсь, эта операция не определено стандартом C, но вероятно, будет работать на большинстве компьютеров и компиляторы. Он неопределен, потому что я считаю, что фактическое число чисел с плавающей запятой зависит от реализации и может быть оставлено в ЦП или компиляторе, и поэтому значение i почти невозможно предсказать после этой операции (то же самое касается значения f в обратном порядке). Это классно используется в функции обратного квадратного корня Джона Кармака для той же гнусной цели.

В любом случае, если вы делаете это в реальном коде, вы должны, вероятно, остановиться и подумать дважды о том, что вы пытаетесь сделать, и почему вы используете float s для этого. Однако, если вы просто делаете это из любопытства, или вы подумали об этом и уверены в своем дизайне и методах, пойдите для этого.

Я убежден, что вы уже знаете, как подсчитать количество бит, заданных в правильном целое, поскольку это намного проще. Если вы не знаете, ваш компилятор (или язык C, я даже не знаю) может иметь функцию для подсчета бит, или вы можете использовать что-то с замечательного веб-сайта Bit-Twiddling Hacks, который имеет способы сделать что-то подобное побитовые операции (которые должны быть довольно быстрыми).

+1

Это не определено не только из-за этого - GCC (в некоторых конфигурациях) будет предупреждать ** разыменованный тип-караульный указатель будет нарушать правила строгого сглаживания ** с серьезной причиной, так как это действительно нарушает предположение (из ISO C, который приносит пользу производительность), что два указателя на отдельные простые типы (кроме символа) никогда не указывают на один и тот же объект. – ephemient

+0

Где находятся два указателя? – sigjuice

+0

'& f' и' (int *) & f' – ephemient

6
#include <stdio.h> /* for printf() */ 
#include <limits.h> /* for CHAR_BIT */ 

int main(void) { 
    /* union method */ 
    { 
    /* a union can only be initialized for the first option in the union */ 
    union { float f; char cs[sizeof(float)]; } const focs = { 1.0 }; 
    int j,k; 
    int count = 0; 
    for (j = 0; j < sizeof(float); j++) 
    { 
     char const byte = focs.cs[j]; 
     for (k = 0; k < CHAR_BIT; k++) 
     { 
     if ((1 << k) & byte) 
     { 
      count++; 
     } 
     } 
    } 
    printf("count(%2.1f) = %d\n", focs.f, count); 
    } 
    /* cast method */ 
    { 
    float const f = 2.5; 
    int j,k; 
    int count = 0; 
    for (j = 0; j < sizeof(float); j++) 
    { 
     char const byte = ((char *)&f)[j]; 
     for (k = 0; k < CHAR_BIT; k++) 
     { 
     if ((1 << k) & byte) 
     { 
      count++; 
     } 
     } 
    } 
    printf("count(%2.1f) = %d\n", f, count); 
    } 
    return 0; 
} 
+0

Я забыл про профсоюз! Но вы должны использовать sizeof (float) вместо sizeof (int). Кроме того, int и float имеют одинаковый размер для большинства систем, но я не могу найти никаких стандартов, говорящих о их относительных размерах (или о размерах поплавка), поэтому я не могу быть уверенным - но не было бы лучше имеет только 1 цикл for() и вместо этого использует объединение float и int? –

+0

Это то, что я считаю реалистичным, они почти всегда одинаковы, но я не знаю ни одного стандарта, который диктует это, поэтому я сделал это так. Я мог бы сжать два цикла, выполнив 'int i; int count = 0; для (i = 0; i <8 * sizeof (float); i ++) {if ((1 << (i% 8)) & focs.cs [i/8]) {count ++; }} ' – rampion

+0

Allthough by this (http://stackoverflow.com/questions/881894/is-char-guaranteed-to-be-exactly-8-bit-long-in-c) Вместо этого я должен использовать' CHAR_BIT' из 8 (хотя я уверен с предположением 'sizeof (char) == 1', так как это по определению). – rampion

3

Хорошая функция для подсчета установленных битов в целое число упомянутых в первом ответе:

int NumberOfSetBits(int i) 
{ 
    i = i - ((i >> 1) & 0x55555555); 
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333); 
    return ((i + (i >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; 
} 

Чтобы использовать его на поплавок вы могли бы сделать что-то вроде этого:

//... 
float f; 
//... 
int numBitsOfF = NumberOfSetBits(*(int*) &f); 
+0

Спасибо всем за поддержку – 2009-07-13 18:47:40

-1
The following function will find the number of bits in a 32-bit number. Just type case your float with integer and call this function by a cast 
float f=3.14f; 
count_bits(*(int *)&f); 

int count_bits(int v) 
{ 
    // count the number of bits set in v 
    int c; // c accumulates the total bits set in v 
    int b=v; 
    for (c = 0; v; c++) 
    { 
      v &= v - 1; // clear the least significant bit set 
    } 
    //printf("No of bits in %d is %d\n",b,c); 
    return c; 
} 
Смежные вопросы