Числа с плавающей точкой.

Автор: Ильдар Валеев

Стандарт регламентирует тип данных с плавающей запятой "float" (32 бита или 4 байта). Такое кодирование используется в стандарте IEEE для нормализованных чисел с плавающей запятой одинарной точности. Следующие три поля описывают число с плавающей запятой одинарной точности:
S: Знак числа. Значения 0 и 1 представляют положительные и отрицательные значения, соответственно. Один бит.
E: Экспонента числа, основание 2. Этому полю отводится 8 бит. Экспонента уменьшается на 127.
F: Дробная часть мантиссы числа, основание 2. 23 бита отведено этому полю.

Таким образом, значение числа с плавающей запятой вычисляется так:

(-1)^S * 2^(E-Bias) * F1

Описание формата:

 float identifier;

+-------+-------+-------+-------+ |byte 0 |byte 1 |byte 2 |byte 3 | Число с плавающей точкой S| E | F | одинарной точности +-------+-------+-------+-------+ 1|<- 8 ->|<-------23 bits------>| <------------32 bits------------>

Смещения начальных битов S, E, и F - это 0, 1, и 9, соответственно. Имейте в виду, что это математические смещения.
F1 - дробное значение, вычисляется следующим образом:
Просматриваются значения битов F.
Если первый бит равен 1, то прибавляется 0.5
Если второй бит равен 1, то прибавляется 0.25
Если третий бит равен 1, то прибавляется 0.125
Т.е. прибавляемые значения уменьшаются на единицу. Этим и ограничивается точность чисел с плавающей точкой.
Примечание:
  1. К этим 23 битам обычно в начало добавляют бит 1 - символизирующий целую часть F1=1.0. Далее происходит прибавление половин от предыдущих номиналов.
  2. Экспоненты 0x00 и 0xFF зарезервированы.
    0x00 используется для представления нуля и ненормализованных величин.
    0xFF используется для представления неопределенных чисел (переполнение) и NaN (Not a Number).

Спецификация IEEE описывает также числа знаковый ноль, знаковая бесконечность (переполнение), и ненормализованные значения (переполнение в обратную сторону). По спецификациям IEEE, "NaN" (не число) является системно-зависимой и не должна интерпретироваться иначе, как "NaN".

Эти выкладки помогут при разработке программного обеспечения для компьютеров и промышленных контроллеров, а также для разработки ПО для АСУТП.

Рассмотрим пример кода на Delphi, реально применяемом и эксплуатируемом в разработанной программе:

function NumToFloat(dwNum: DWORD): String;
const
  EXP_BIAS = 127;
var
  sFloat: String;
  dwFloat: Cardinal;
  snglFloat: Single; // 4 байта
  bySign, byExp: Byte;
  dwFractPart: Cardinal;
  snglFractPart: Single;
begin
  dwFloat:= dwNum;
  bySign:= (dwFloat shr 31) and $1;
  byExp:= Byte((dwFloat shr 23) and $FF);
  if byExp = $FF then
  begin
    // ShowMessage('Infinity or Nan');
    Result:= 'Infinity or Nan';
  end
  else
  if byExp = $00 then
  begin
    // ShowMessage('0 (Zero) or Denormal');
    Result:= '0 (Zero) or Denormal';
  end
  else
  begin
    dwFractPart:= Cardinal( (  Cardinal(dwFloat shl 9) shr 9  ) );

    // 10 бит точность - тысячные доли - итоговая точность сотые доли
    snglFractPart:= 1.0 +
      ((dwFractPart shr 22) and $1) * 0.5 +
      ((dwFractPart shr 21) and $1) * 0.25 +
      ((dwFractPart shr 20) and $1) * 0.125 +
      ((dwFractPart shr 19) and $1) * 0.0625 +
      ((dwFractPart shr 18) and $1) * 0.03125 +
      ((dwFractPart shr 17) and $1) * 0.015625 +
      ((dwFractPart shr 16) and $1) * 0.0078125 +
      ((dwFractPart shr 15) and $1) * 0.00390625 +
      ((dwFractPart shr 14) and $1) * 0.001953125 +
      ((dwFractPart shr 13) and $1) * 0.0009765625 +
      // Максимальная точность
      ((dwFractPart shr 12) and $1) * 0.00048828125 +
      ((dwFractPart shr 11) and $1) * 0.000244140625 +
      ((dwFractPart shr 10) and $1) * 0.0001220703125 +
      ((dwFractPart shr 9) and  $1) * 0.00006103515625 +
      ((dwFractPart shr 8) and  $1) * 0.000030517578125 +
      ((dwFractPart shr 7) and  $1) * 0.0000152587890625 +
      ((dwFractPart shr 6) and  $1) * 0.00000762939453125 +
      ((dwFractPart shr 5) and  $1) * 0.000003814697265625 +
      ((dwFractPart shr 4) and  $1) * 0.0000019073486328125 +
      ((dwFractPart shr 3) and  $1) * 0.00000095367431640625 +
      ((dwFractPart shr 2) and  $1) * 0.000000476837158203125 +
      ((dwFractPart shr 1) and  $1) * 0.0000002384185791015625 +
      ( dwFractPart        and  $1) * 0.00000011920928955078125;

    snglFloat:= Power(-1, bySign) * Power(2.0, (byExp - EXP_BIAS)) * snglFractPart;
    sFloat:= FloatToStr(snglFloat);
    // ShowMessage(sFloat);
  end;
  Result:= sFloat;
end;
Был разработан пример, который переводит десятичное представление такого числа в его дробное значение. В десятичное представление можно привести заранее при помощи кода:
dwFloat:= (Byte(#$41) shl 24) + (Byte(#$F1) shl 16) + (Byte(#$EB) shl 8) + Byte(#$85);
где числа могут быть взяты путем побайтного доступа к строке, содержащей число, например, сырое значение из контроллера. Скриншот программы для получения значения чисел с плавающей запятой:

Скриншот примера, расчёт числа с плавающей запятой

Скачать пример с исходниками можно по ссылке: Пример с исходниками


Google