#c
#c
Вопрос:
Проблема
Мне нужно умножить число без использования оператора *
or
или других библиотек, только двоичную логику
Чтобы умножить число на два, используя норму IEEE, вы добавляете единицу к показателю степени, например:
12 = 1 10000010 100000(...)
Итак, показатель равен: 10000010
(130)
Если я хочу умножить его на 2, я просто добавляю к нему 1, и оно становится 10000011
(131).
Вопрос
Если я получу значение с плавающей точкой, как мне преобразовать его в двоичный файл, а затем в норму IEEE? Пример: 8.0 = 1000.0
в IEEE мне нужно, чтобы в левой части было только одно число, поэтому 1.000 * 2^3
. Тогда как мне добавить единицу, чтобы умножить ее на 2?
Мне нужно получить значение с плавающей точкой, т. Е. 6.5
Преобразуем его в двоичный 110.1
Затем в IEEE 754 0 10000001 101000(...)
Извлеките показатель степени 10000001
Добавьте к нему единицу 10000010
Верните его в IEEE 754 0 10000010 101000(...)
Затем вернитесь к значению float 13
Комментарии:
1. Зачем вам нужно умножать число на два без использования оператора умножения? Это какая-то проблема?
2. Вы могли бы просто добавить его к самому себе, если вам нужно умножать без использования
*
.3. Используйте объединение с int и float, добавьте 1<<23 к int, затем получите значение float.
4. Поэтому используйте повторяющиеся добавления:D
5. Вы знаете, что
a a == a * 2
Ответ №1:
Учитывая, что известно, что реализация C использует базовый 32-разрядный двоичный код IEEE-754 с плавающей запятой для своего float
типа, следующий код показывает, как разделить биты, представляющие a float
, настроить показатель степени и повторно собрать биты. Обрабатываются только простые умножения, включающие обычные числа.
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int main(void)
{
float f = 6.125;
// Copy the bits that represent the float f into a 32-bit integer.
uint32_t u;
assert(sizeof f == sizeof u);
memcpy(amp;u, amp;f, sizeof u);
// Extract the sign, exponent, and significand fields.
uint32_t sign = u >> 31;
uint32_t exponent = (u >> 23) amp; 0xff;
uint32_t significand = u amp; 0x7fffff;
// Assert the exponent field is in the normal range and will remain so.
assert(0 < exponent amp;amp; exponent < 254);
// Increment the exponent.
exponent;
// Reassemble the bits and copy them back into f.
u = sign << 31 | exponent << 23 | significand;
memcpy(amp;f, amp;u, sizeof f);
// Display the result.
printf("%gn", f);
}
Комментарии:
1. Эй, отличный ответ, я почерпнул из него хорошую идею, но я не могу использовать другие библиотеки для решения этой проблемы. Только <stdio.h>
Ответ №2:
Возможно, это не совсем то, что вы ищете, но C имеет библиотечную функцию ldexp
, которая делает именно то, что вам нужно:
double x = 6.5;
x = ldexp(x, 1); // now x is 13
Ответ №3:
Возможно, объединения — это тот инструмент, который вам нужен.
#include<iostream>
union fb {
float f;
struct b_s {
unsigned int sign :1;
unsigned int mant :22;
unsigned int exp :8;
} b;
};
fb num;
int main() {
num.f = 3.1415;
num.b.exp ;
std::cout << num.f << std::endl;
return 0;
}
Комментарии:
1. Здесь не используйте битовые поля. Расположение битового поля не определено в соответствии со стандартом C : «Реализация может выделить любой адресуемый блок памяти, достаточно большой, чтобы вместить битовое поле. Если остается достаточно места, битовое поле, которое непосредственно следует за другим битовым полем в структуре, должно быть упаковано в соседние биты того же блока. Если остается недостаточно места, переносится ли битовое поле, которое не помещается, в следующий блок или перекрывает соседние блоки, определяется реализацией. …
2. (продолжение) Порядок распределения битовых полей внутри единицы (от старшего порядка к младшему или от младшего порядка к старшему порядку) определяется реализацией . Выравнивание адресуемой единицы хранения не указано .» Битовые поля ужасно непереносимы, и вы просто не можете с какой-либо уверенностью утверждать, что ваше
sign
битовое поле находится где-либо рядом с фактическим битом знака значения с плавающей запятойf
.3. Это давнее злоупотребление объединениями и имеет неопределенное поведение.
4. В комментариях к коленному рефлексу упущены важные факты о том, что поле significand имеет неправильный размер (должно быть 23, а не 22) и поля расположены не по порядку (должно быть sign-exponent-significand или наоборот, в зависимости). Код напечатал только 6.283, потому что слишком короткое поле significand компенсировало неправильное расположение поля sign, оставляя показатель степени в нужном месте.
5. @LightnessRacesinOrbit: Стандарт C определяет доступ к объединяющему элементу, отличному от последнего, записанного для переосмысления байтов.