HannesNZ
HannesNZ

Reputation: 165

Fixing Floating Point Error

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 have 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.

Upvotes: 2

Views: 2175

Answers (4)

Rick Regan
Rick Regan

Reputation: 3512

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.

Upvotes: 0

Ken Bourassa
Ken Bourassa

Reputation: 6467

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;

Upvotes: 0

gabr
gabr

Reputation: 26830

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.

Upvotes: 1

awmross
awmross

Reputation: 3839

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.

Upvotes: 1

Related Questions