16 位浮点运行 Pascal/Delphi


本文整理自网络,侵删。

 
具有16位精度的浮点数主要用于计算机图形学。它们也被称为半精度浮点数(有一半的精度为32位浮点数)。这里有一个符号位,5位指数,还有10位为mantissa。由于有限的精度(在普通的cpus/fpus中没有支持),半浮点数实际上并不是用于计算计算的。在2000年代初,有一半的漂浮物出现在图像和纹理的样本中。float提供的动态范围比常规的8位或16位的整数示例要高。另一方面,常用的单和双精度浮点数的每个像素的内存成本要高得多。一半的浮动有更合理的内存要求,并且它们的精度对于许多在成像方面的应用来说是足够的。多年来,ATI和NVidia GPUs一直支持16bit的浮动格式。我不确定其他的ihv,但是至少Direct3D 10有能力的gpu都应该支持它。如果您对如何在一半和单个精度浮点数之间转换(对象Pascal代码)感兴趣,请继续阅读。最后,半/单转换代码,这里是转换成半浮点数的代码,并返回到单个精度浮点数。它基于OpenEXR库中的C++代码(半类)。首先,一些类型和常量注意到,地中海式的类型只是Word的别名。

type
  THalfFloat = type Word;
 
const
  HalfMin:     Single = 5.96046448e-08; // Smallest positive half
  HalfMinNorm: Single = 6.10351562e-05; // Smallest positive normalized half
  HalfMax:     Single = 65504.0;        // Largest positive half
  // Smallest positive e for which half (1.0 + e) != half (1.0)
  HalfEpsilon: Single = 0.00097656;
  HalfNaN:     THalfFloat = 65535;
  HalfPosInf:  THalfFloat = 31744;
  HalfNegInf:  THalfFloat = 64512;

function FloatToHalf(Float: Single): THalfFloat;
var
  Src: LongWord;
  Sign, Exp, Mantissa: LongInt;
begin
  Src := PLongWord(@Float)^;
  // Extract sign, exponent, and mantissa from Single number
  Sign := Src shr 31;
  Exp := LongInt((Src and $7F800000) shr 23) - 127 + 15;
  Mantissa := Src and $007FFFFF;
 
  if (Exp > 0) and (Exp < 30) then
  begin
    // Simple case - round the significand and combine it with the sign and exponent
    Result := (Sign shl 15) or (Exp shl 10) or ((Mantissa + $00001000) shr 13);
  end
  else if Src = 0 then
  begin
    // Input float is zero - return zero
    Result := 0;
  end
  else
  begin
    // Difficult case - lengthy conversion
    if Exp <= 0 then
    begin
      if Exp < -10 then
      begin        
        // Input float's value is less than HalfMin, return zero
         Result := 0;
      end
      else
      begin
        // Float is a normalized Single whose magnitude is less than HalfNormMin.  
        // We convert it to denormalized half.
        Mantissa := (Mantissa or $00800000) shr (1 - Exp);
        // Round to nearest
        if (Mantissa and $00001000) > 0 then
          Mantissa := Mantissa + $00002000;
        // Assemble Sign and Mantissa (Exp is zero to get denormalized number)
        Result := (Sign shl 15) or (Mantissa shr 13);
      end;
    end
    else if Exp = 255 - 127 + 15 then
    begin
      if Mantissa = 0 then
      begin
        // Input float is infinity, create infinity half with original sign
        Result := (Sign shl 15) or $7C00;
      end
      else
      begin
        // Input float is NaN, create half NaN with original sign and mantissa
        Result := (Sign shl 15) or $7C00 or (Mantissa shr 13);
      end;
    end
    else
    begin
      // Exp is > 0 so input float is normalized Single
 
      // Round to nearest
      if (Mantissa and $00001000) > 0 then
      begin
        Mantissa := Mantissa + $00002000;
        if (Mantissa and $00800000) > 0 then
        begin
          Mantissa := 0;
          Exp := Exp + 1;
        end;
      end;
 
      if Exp > 30 then
      begin
        // Exponent overflow - return infinity half
        Result := (Sign shl 15) or $7C00;
      end
      else
        // Assemble normalized half
        Result := (Sign shl 15) or (Exp shl 10) or (Mantissa shr 13);
    end;
  end;
end;

function HalfToFloat(Half: THalfFloat): Single;
var
  Dst, Sign, Mantissa: LongWord;
  Exp: LongInt;
begin
  // Extract sign, exponent, and mantissa from half number
  Sign := Half shr 15;
  Exp := (Half and $7C00) shr 10;
  Mantissa := Half and 1023;
 
  if (Exp > 0) and (Exp < 31) then
  begin
    // Common normalized number
    Exp := Exp + (127 - 15);
    Mantissa := Mantissa shl 13;
    Dst := (Sign shl 31) or (LongWord(Exp) shl 23) or Mantissa;
    // Result := Power(-1, Sign) * Power(2, Exp - 15) * (1 + Mantissa / 1024);
  end
  else if (Exp = 0) and (Mantissa = 0) then
  begin
    // Zero - preserve sign
    Dst := Sign shl 31;
  end
  else if (Exp = 0) and (Mantissa <> 0) then
  begin
    // Denormalized number - renormalize it
    while (Mantissa and $00000400) = 0 do
    begin
      Mantissa := Mantissa shl 1;
      Dec(Exp);
    end;
    Inc(Exp);
    Mantissa := Mantissa and not $00000400;
    // Now assemble normalized number
    Exp := Exp + (127 - 15);
    Mantissa := Mantissa shl 13;
    Dst := (Sign shl 31) or (LongWord(Exp) shl 23) or Mantissa;
    // Result := Power(-1, Sign) * Power(2, -14) * (Mantissa / 1024);
  end
  else if (Exp = 31) and (Mantissa = 0) then
  begin
    // +/- infinity
    Dst := (Sign shl 31) or $7F800000;
  end
  else //if (Exp = 31) and (Mantisa <> 0) then
  begin
    // Not a number - preserve sign and mantissa
    Dst := (Sign shl 31) or $7F800000 or (Mantissa shl 13);
  end;
 
  // Reinterpret LongWord as Single
  Result := PSingle(@Dst)^;
end;


program Half_test;
{$APPTYPE CONSOLE}
{$Include HalfFloat.pas}
var
X, Z : single;
Y : THalfFloat;
begin { TODO -oUser -cConsole Main : Insert code here }
decimalseparator := ‘.’;
x := 1439.156;
y := FloatToHalf(X);
z := HalfToFloat(y);
writeln(x:10:3);
writeln(z:10:3);
readln;
end.

相关阅读 >>

Delphi dbnavigator控件的按钮显示成中文

Delphi打开"我的电脑"等特殊文件夹

Delphi xe10 文件目录/路径操作 (andorid、ios、windows)

Delphi chart组件,chart控件用法

Delphi 根据关键字取关键词以后的字符串

Delphi 读写文本文件

Delphi 结束360safe和360保险箱进程

正则表达式初学入门

Delphi f1026 file not found: ''quickrpt.dcu''解决方法

Delphi动态建立panel无法更改颜色?

更多相关阅读请进入《Delphi》频道 >>



打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...