1

Trying to round off double values to two decimal places using function FormatFloat (Format string '0.##').

Below are the input and output values

231.545 -> 231.54 (but expected output is 231.55)
2.315 -> 2.31 (but expected output is 2.32)

23.045 -> 23.05 (gives expected output 23.05)
23.145 -> 23.14 (but expected output 23.15)

23.245 -> 23.25 (gives expected output 23.25)
23.345 -> 23.34 (but expected output 23.35)

23.445 -> 23.45 (gives expected output 23.45)
23.545 -> 23.55 (gives expected output 23.55)
23.645 -> 23.65 (but expected output 23.64)

23.745 -> 23.75 (gives expected output 23.75)
23.845 -> 23.84 (but expected output 23.84)
23.945 -> 23.95 (gives expected output 23.95)

why this strange behaviour happening? am using Delphi 7.

bejarun
  • 175
  • 3
  • 11
  • Binary floating point values can not represent every value exactly. This is what you are seeing. – LU RD Dec 10 '18 at 15:04
  • is there any other function get the exact expected value? (or) do we need to write the our own? – bejarun Dec 10 '18 at 15:09
  • Use a decimal data type. There are libraries around or use currency. See [BigDecimals](http://rvelthuis.de/programs/bigdecimals.html) – LU RD Dec 10 '18 at 15:14
  • @LURD: I don't think my BigDecimals will compile in Delphi 7. I don't think D7 knows records with methods. It would have to be rewritten to use plain functions. – Rudy Velthuis Dec 10 '18 at 15:24

1 Answers1

5

Binary floating point values can not represent every value exactly. This is what you are seeing.

For example, the value 2.315 is represented in double precision by:

2.31499 99999 99999 94670 92948 17992 48605 96656 79931 64062 5

This will be rounded to 2.31


If you can use a decimal data type, like currency, you can get the wanted output (if currency is within limits of your working range):

var
  c : Currency;
begin
  c := 2.315;
  WriteLn(FormatFloat('0.##',c)); // Outputs 2.32
end.

An alternative is to use a decimals library, like BigDecimals, but that would require a modern Delphi version with support of records with methods.

LU RD
  • 34,438
  • 5
  • 88
  • 296
  • 1
    FWIW, for Win32, an alternative is my [Decimals](http://rvelthuis.de/programs/decimals.html) type. Limited range (10^28 .. 10^-28), but faster and smaller (128 bit) than BigDecimal and much more precise than floating point. – Rudy Velthuis Dec 10 '18 at 16:03
  • @LU RD : FormatFloat function definition looks like accepting Extended data type, if I use Extended instead of double will it solve this problem?.. Currency data type is not within of my working range) `function FormatFloat(const Format: string; Value: Extended): string;` – bejarun Dec 12 '18 at 13:19
  • @LU RD: How can I get `2.31499 99999 99999 94670 92948 17992 48605 96656 79931 64062 5` for `2.315` using Delphi code?,, `FormatFloat` function calls `FoatToTextFmt` to do the actual formatting.. there we have the `assembly code`, which I can't understand... – bejarun Dec 12 '18 at 14:01
  • There is an exact float to string calculator [here](http://pages.cs.wisc.edu/~rkennedy/exact-float?number=2.335). Also, look at [ID: 19421, ExactFloatToStr_JH0 -- Exact Float to String Routines](http://cc.embarcadero.com/Item.aspx?id=19421) for John Herbsters's Delphi code. – LU RD Dec 12 '18 at 14:10
  • I'm afraid that using extended, will have similar problems as double, but at another level. There may be some others alternatives [here](https://stackoverflow.com/q/9690133/576719) – LU RD Dec 12 '18 at 14:23