开发者

Fixing Floating Point Error

开发者 https://www.devze.com 2023-01-01 08:42 出处:网络
I have some code that gets the leading value (non-zero) of a Double using normal math instead of String Math...

I have some code that gets the leading value (non-zero) of a Double using normal math instead of String Math...

For Example:

0.020 would return 2

3.12 would return 3

1000 should return 1

The code I hav开发者_开发知识库e at the moment is:

LeadingValue := Trunc(ResultValue * Power(10, -(Floor(Log10(ResultValue)))))

However when ResultValue is 1000 then LeadingValue ends up as 0.

What can I do to fix this problem I'm assuming is being caused by floating point errors?

Thanks.


I'm curious as to why you can't / won't use String manipulation for this. Could you explain further?

The most obvious thing you can do to fix the problem is to convert the number to a String and find the first non zero digit.


A possible way to cheat around the rounding problems:

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Math;

function LeadingValue(num: extended): integer;
const
  ExtendedResolution = 1E-19 * 1000; // from Math
begin
  num := Abs(num);
  Result := Trunc(num * Power(10, -(Floor(Log10(num)))) + ExtendedResolution);
end;

begin
  try
    Writeln(LeadingValue(0.02));
    Writeln(LeadingValue(3.12));
    Writeln(LeadingValue(1000));
    Writeln(LeadingValue(0.9999999999));
    Writeln(LeadingValue(1.0000000001));
    Writeln(LeadingValue(-0.02));
    Writeln(LeadingValue(-3.12));
    Writeln(LeadingValue(-1000));
    Writeln(LeadingValue(-0.9999999999));
    Writeln(LeadingValue(-1.0000000001));
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.


if you divide instead of multiply, it fixes your test cases. Overall, it will yield better result as the "Power" result won't have a fractional value. I'm afraid it might not have a 100% case coverage, but unless a better solution is proposed, this should be better than what you have.

function Func(Value : Double) : Integer;
begin
  Result := Trunc(Value / Power(10, (Floor(Log10(Value)))))
end;


You're not going to be able to do this with 100% accuracy using floating-point math. What you need to do essentially is to convert a binary floating-point value to a decimal string. Routines that do that accurately, like David Gay's dtoa.c, use high precision (integer) arithmetic.

0

精彩评论

暂无评论...
验证码 换一张
取 消