Recent

Author Topic: Wrong numbers with "Format" and Floats  (Read 628 times)

Bob Andrews

  • Newbie
  • Posts: 6
Wrong numbers with "Format" and Floats
« on: July 05, 2026, 09:32:27 am »
Hello, I am new here! :)

I am having a problem with Format. The line

Code: Pascal  [Select][+][-]
  1. Caption := Format('%0.2f', [7.5 * 1000000000]);

should return 7500000000.00. But it does not. It returns 7500000256.00 instead. And I just don't get why.

The weird thing is, when I use a 7.6 or a 7.3 for example, the outcome is correct. When using .5 numbers the only times it's right, is with a 0.5 or a 1.5 or a 2.5 or a 3.5. Any other .5 number will somehow mess with the outcome. FloatToStr or FloatToStrF will give the same results respectively.

I'm on Lazarus 4.8.0.0 and Free Pascal 3.2.2 under Linux Mint 22.3, 64 Bit.

But just for reference I also tried it with Win2000 Pro 32 Bit and Win11 64 Bit, with both the exact same results.

Does anybody know what the issue is, or how I could resolve this?
« Last Edit: July 05, 2026, 09:35:42 am by Bob Andrews »

Josh

  • Hero Member
  • *****
  • Posts: 1460
Re: Wrong numbers with "Format" and Floats
« Reply #1 on: July 05, 2026, 09:57:56 am »
I suspect FPC is choosing minimum to fit ie single as evals left to right sees 7.5 sets as single.

u could try casting to int64, should force a double

Caption := Format('%0.2f', [7.5 * Int64(1000000000)]);

or

change order and add .0 to end ie 1000000000.0 should set it double

Caption := Format('%0.2f', [1000000000.0 * 7.5)])
« Last Edit: July 05, 2026, 10:00:50 am by Josh »
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

Bob Andrews

  • Newbie
  • Posts: 6
Re: Wrong numbers with "Format" and Floats
« Reply #2 on: July 05, 2026, 10:03:45 am »
Thanks Josh,

but both of your hints don't change anything. BUT you sparked an idea. Using

Code: Pascal  [Select][+][-]
  1. Caption := Format('%0.2f', [Double(7.5) * 1000000000]);

works!

Thanks :)

Josh

  • Hero Member
  • *****
  • Posts: 1460
Re: Wrong numbers with "Format" and Floats
« Reply #3 on: July 05, 2026, 11:00:10 am »
Glad it sparked an idea.

Unfortunately wasn't by a computer with fpc on, so was just thinking out aloud, casting to double would have been next option, probable better that way as your forcing the double on the expression stopping fpc casting lower type.

or u could set the Multiplier and BaseValue as constant, as you can lock the type and values in.

const
  Multiplier:Double=7.5; // Locked to 64bit Double
  BaseValue:Int64=1000000000; // Locked to 64bit Integer
begin
  Caption := Format('%0.2f', [Multiplier * BaseValue]);
end;
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

Bob Andrews

  • Newbie
  • Posts: 6
Re: Wrong numbers with "Format" and Floats
« Reply #4 on: July 05, 2026, 03:27:16 pm »
Hey Josh,

well, in my case I need that to be flexible, because both numbers that get multiplied with each other change during runtime again and again. I was just simplifying it here for better readability. So what I did was declare the "7.5" side as a Double variable, and the other side gets into round brackets forcing it to be Double:

Code: Pascal  [Select][+][-]
  1. dblVar * Double(1 * 2 * 3 * 4 * 5)

Of course the code is - again - simplified here for readability. :)

This works great for my use. Still it's a little bugging me, why it wouldn't work as Single. And also, that, while being a Double, it uses a bit more RAM, although I would actually just need it as Single...

LeP

  • Sr. Member
  • ****
  • Posts: 437
Re: Wrong numbers with "Format" and Floats
« Reply #5 on: July 05, 2026, 03:39:31 pm »
Strange ... in Delphi it works perfect in all cases, if you force single for every number too.

The only way to give an incorrect result (i.e. the one indicated by the writer) is:
Code: Pascal  [Select][+][-]
  1. Caption := Format('%0.2f', [Single(7.5 * 1000000000)]);

This should be taken into account for anyone converting Delphi code to FPC.

Can this be considered a bug?
« Last Edit: July 05, 2026, 03:43:24 pm by LeP »
Un Sistema per domarli, un IDE per trovarli, un codice per ghermirli e nel framework incatenarli.
An operating system to tame them, an IDE to find them, a code to catch them and in the framework chain them.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12537
  • Debugger - SynEdit - and more
    • wiki
Re: Wrong numbers with "Format" and Floats
« Reply #6 on: July 05, 2026, 09:11:29 pm »
Yes some float numbers that can have finite digits in decimal, are infinite in binary. Quite famous 0.4
IIRC there is a "famous" minecraft bug caused by it...

wp

  • Hero Member
  • *****
  • Posts: 13628
Re: Wrong numbers with "Format" and Floats
« Reply #7 on: July 06, 2026, 12:54:34 am »
Run this test in Lazarus/Free Pascal:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2. begin
  3.   WriteLn(SizeOf(4.5));
  4.   ReadLn;
  5. end.
The output is 4 indicating that the literal value 4.5 is handled as single type - because single has the shortest byte length needed to express 4.5.

Delphi is completely different. When you run the test project in Delphi, the output is 10 (on 32-bit platform, or 8 on 64-bit platform), indicating that it defaults float literals to the largest floating point type available (extended on 32bit, or double on 64bit).

Khrys

  • Sr. Member
  • ****
  • Posts: 470
Re: Wrong numbers with "Format" and Floats
« Reply #8 on: July 06, 2026, 07:58:57 am »
The exact type of any number literal in Free Pascal depends entirely on its content, be it integers or floats (not a fan of the latter).
The  {$minfpconstprec}  directive can be used to place a lower bound on the assumed precision of floating-point literals. Apart from that, the only way to make float constant typing not seem vibe-based is to cast each and every literal individually.

Bob Andrews

  • Newbie
  • Posts: 6
Re: Wrong numbers with "Format" and Floats
« Reply #9 on: July 06, 2026, 09:32:41 am »
The exact type of any number literal in Free Pascal depends entirely on its content, be it integers or floats (not a fan of the latter).
The  {$minfpconstprec}  directive can be used to place a lower bound on the assumed precision of floating-point literals. Apart from that, the only way to make float constant typing not seem vibe-based is to cast each and every literal individually.

So that I get this right: If I use Singles to multiply, the outcome will by treated as Single, no matter if the outcome would be incorrect? I mean, lets say, I would use two numbers that are set to 255 (Byte) and I would multiply them (255 x 255 = 65025) I WOULD NOT get 65025, because that would be at least Word? But since I decleared Byte it wouldn't fit because the outcome would be treated as Byte?

Or am I mistaken here?

rvk

  • Hero Member
  • *****
  • Posts: 7063
Re: Wrong numbers with "Format" and Floats
« Reply #10 on: July 06, 2026, 10:20:21 am »
So that I get this right: If I use Singles to multiply, the outcome will by treated as Single, no matter if the outcome would be incorrect? I mean, lets say, I would use two numbers that are set to 255 (Byte) and I would multiply them (255 x 255 = 65025) I WOULD NOT get 65025, because that would be at least Word? But since I decleared Byte it wouldn't fit because the outcome would be treated as Byte?
No. 255 * 255 doesn't fit in a Byte. Otherwise the result (during calculation it's always done with bigger types) doesn't fit, so it will be put in a Word.

7.5 * 1000000000.0 does fit in a Single so it will force the result (which is 7500000000.0) back into a Single, resulting in 7500000256.00.

For example... this 7000000000.5 * 1000000000.0 will not fit in a Single and that would be put in a Double.

(and even when putting 7000000000500000000.00 in a double... you get 7000000000499999700.00)

Welcome to the world of IEEE-754   ;)
https://en.wikipedia.org/wiki/IEEE_754
« Last Edit: July 06, 2026, 10:26:05 am by rvk »

LeP

  • Sr. Member
  • ****
  • Posts: 437
Re: Wrong numbers with "Format" and Floats
« Reply #11 on: July 06, 2026, 11:03:33 am »
The output is 4 indicating that the literal value 4.5 is handled as single type - because single has the shortest byte length needed to express 4.5.
Delphi is completely different. When you run the test project in Delphi, the output is 10 (on 32-bit platform, or 8 on 64-bit platform), indicating that it defaults float literals to the largest floating point type available (extended on 32bit, or double on 64bit).

This is about my question of a bug: in fpc Delphi mode too this doesn't work like in Delphi.

Take care, in Delphi:

Code: Pascal  [Select][+][-]
  1. Caption := Format('%0.2f', [Single(7.5) * 1000000000]);

Give the right (logical expected) result -> 7500000000.00

and this code do the same (I don't expect that on global var there will be some optimization like may be a "mul" with literal constant):

Code: Pascal  [Select][+][-]
  1. var
  2.   Form1: TForm1;
  3.   p: single;
  4.  
  5. implementation
  6.  
  7. {$R *.dfm}
  8.  
  9. procedure TForm1.Button1Click(Sender: TObject);
  10. begin
  11.   Caption := Format('%0.2f', [p * 1000000000]);
  12. end;
  13.  
  14. procedure TForm1.FormCreate(Sender: TObject);
  15. begin
  16.   p := 7.5;
  17. end;
  18.  

This in debug mode without any optimization.

I don't know who is right, but this may be influence some apps ?
Un Sistema per domarli, un IDE per trovarli, un codice per ghermirli e nel framework incatenarli.
An operating system to tame them, an IDE to find them, a code to catch them and in the framework chain them.

Bob Andrews

  • Newbie
  • Posts: 6
Re: Wrong numbers with "Format" and Floats
« Reply #12 on: July 06, 2026, 11:11:53 am »
7.5 * 1000000000.0 does fit in a Single so it will force the result (which is 7500000000.0) back into a Single, resulting in 7500000256.00.

That's the part I don't get. It fit's in a Single just fine, but still the outcome is off? Because of some kind of rounding error? I mean, it's 7.5, not 7.500001 or something. So I really don't understand this issue at all. OK, I got a solution and I am fine with it, but still I would like to know what's up with that.

Or in other words: Is Lazarus or FPC doing something wrong? Or is Delphi? Or are both correct but just going different routes - and none of them are wrong per se? Or am I just not getting it!? :)

rvk

  • Hero Member
  • *****
  • Posts: 7063
Re: Wrong numbers with "Format" and Floats
« Reply #13 on: July 06, 2026, 11:26:46 am »
7.5 * 1000000000.0 does fit in a Single so it will force the result (which is 7500000000.0) back into a Single, resulting in 7500000256.00.

That's the part I don't get. It fit's in a Single just fine, but still the outcome is off? Because of some kind of rounding error?
Yeah... but that's the whole point of Single and IEEE-754.
You can't binary put 7500000000.00 into a single (because of bit representation).

You can see that here https://www.h-schmidt.net/FloatConverter/IEEE754.html
Put the 7500000000.00 into the decimal field and see what the result is.

Or in other words: Is Lazarus or FPC doing something wrong? Or is Delphi? Or are both correct but just going different routes - and none of them are wrong per se? Or am I just not getting it!? :)
Yes, they go a different route.

If you want FPC to behave the same... you can use (at least with FPC 3.3.1+, not sure about older versions)
Code: Pascal  [Select][+][-]
  1. {$EXCESSPRECISION ON} // This tells FPC to upscale Single operations to Double precision
« Last Edit: July 06, 2026, 11:28:53 am by rvk »

Khrys

  • Sr. Member
  • ****
  • Posts: 470
Re: Wrong numbers with "Format" and Floats
« Reply #14 on: July 06, 2026, 11:28:56 am »
The exact type of any number literal in Free Pascal depends entirely on its content, be it integers or floats (not a fan of the latter).
The  {$minfpconstprec}  directive can be used to place a lower bound on the assumed precision of floating-point literals. Apart from that, the only way to make float constant typing not seem vibe-based is to cast each and every literal individually.

So that I get this right: If I use Singles to multiply, the outcome will by treated as Single, no matter if the outcome would be incorrect? I mean, lets say, I would use two numbers that are set to 255 (Byte) and I would multiply them (255 x 255 = 65025) I WOULD NOT get 65025, because that would be at least Word? But since I decleared Byte it wouldn't fit because the outcome would be treated as Byte?

Or am I mistaken here?

For integer types, this system actually works pretty well due to implicit type promotion in expressions (as @rvk pointed out) and also because integer types are pretty much interchangeable in all aspects except range.
With floats it's not as simple - switching to a bigger floating-point type doesn't just increase range, but density as well. The change isn't purely additive (like e.g. increasing the range from 255 to 65536 when going from bytes to words), but distributed throughout the entire extended real number line. Additionally, there's no automatic promotion from  Single  to  Double  in expressions unless you use  {$excessprecision on}.

In the following example, FPC infers the type of  16777216.0  as  Single,  ultimately returning  16777216  due to precision issues:

Code: Pascal  [Select][+][-]
  1. function Foo(N: Integer = 1): String;
  2. begin
  3.   Result := FloatToStr(16777216.0 + N);
  4. end;

Casting the literal to  Double  returns the correct result  (16777217):

Code: Pascal  [Select][+][-]
  1. function Foo(N: Integer = 1): String;
  2. begin
  3.   Result := FloatToStr(Double(16777216.0) + N);
  4. end;

 

TinyPortal © 2005-2018