View Issue Details

IDProjectCategoryView StatusLast Update
0030744FPCRTLpublic2016-11-26 17:58
ReporterThaddy de KoningAssigned ToMichael Van Canneyt 
PrioritynormalSeverityminorReproducibilityN/A
Status resolvedResolutionfixed 
PlatformallOSallOS Versionall
Product Version3.1.1Product Build 
Target Version3.2.0Fixed in Version3.1.1 
Summary0030744: fmod missing from math unit
DescriptionThere no floating point modulo in the math unit.

suggest to add it as operator as it is basically a one liner:

  operator mod(const a,b:float) c:float;inline;
  begin
    c:= a-b * floor(a/b);
  end;
TagsNo tags attached.
Fixed in Revision34968
FPCOldBugId
FPCTarget
Attached Files
  • fmodtest.pp (986 bytes)
    program fmodtest;
    {$mode objfpc}
    uses math;
    var
    &as,bs:single;
    ad,bd:double;
    ae,be:extended;
    
    begin
    &as:=18.5;ad:=18.5;ae:=18.5;
    bs:=4.2;bd:=4.2;be:=4.2;
    // simple modulo
    writeln(&as mod bs :4:8);
    writeln(ad mod bd :4:8);
    writeln(ae mod be :4:8);
    
    // single
    writeln(fmod(&as,bs) :4:8);
    // double
    writeln(fmod(ad,bd) :4:8);
    //Extended
    writeln(fmod(ae,be) :4:8);
    
    // numerator sign changed. Sign should be numerator sign
    &as:=-18.5;ad:=-18.5;ae:=-18.5;
    // simple modulo
    writeln(&as mod bs :4:8);
    writeln(ad mod bd :4:8);
    writeln(ae mod be :4:8);
    
    // single
    writeln(fmod(&as,bs) :4:8);
    // double
    writeln(fmod(ad,bd) :4:8);
    //Extended
    writeln(fmod(ae,be) :4:8);
    
    // denominator sign changed. Sign should be numerator sign
    &as:=18.5;ad:=18.5;ae:=18.5;
    bs:=-4.2;bd:=-4.2;be:=-4.2;
    // simple modulo
    writeln(&as mod bs :4:8);
    writeln(ad mod bd :4:8);
    writeln(ae mod be :4:8);
    // single
    writeln(fmod(&as,bs) :4:8);
    // double
    writeln(fmod(ad,bd) :4:8);
    //Extended
    writeln(fmod(ae,be) :4:8);
    end.
    
    fmodtest.pp (986 bytes)
  • fmodpatch.patch (1,654 bytes)
    Index: math.pp
    ===================================================================
    --- math.pp	(revision 34924)
    +++ math.pp	(working copy)
    @@ -192,6 +192,21 @@
     procedure DivMod(Dividend: DWord; Divisor: DWord; var Result, Remainder: DWord);
     procedure DivMod(Dividend: LongInt; Divisor: LongInt; var Result, Remainder: LongInt);
     
    +{ Floating point modulo}
    +{$ifdef FPC_HAS_TYPE_SINGLE}
    +function FMod(const a, b: Single): Single;inline;overload;
    +{$endif FPC_HAS_TYPE_SINGLE}
    +
    +{$ifdef FPC_HAS_TYPE_DOUBLE}
    +function FMod(const a, b: Double): Double;inline;overload;
    +{$endif FPC_HAS_TYPE_DOUBLE}
    +
    +{$ifdef FPC_HAS_TYPE_EXTENDED}
    +function FMod(const a, b: Extended): Extended;inline;overload;
    +{$endif FPC_HAS_TYPE_EXTENDED}
    +
    +operator mod(const a,b:float) c:float;inline;
    +
     // Sign functions
     Type
       TValueSign = -1..1;
    @@ -2380,7 +2395,33 @@
     end;
     {$endif FPC_MATH_HAS_DIVMOD}
     
    +{ Floating point modulo}
    +{$ifdef FPC_HAS_TYPE_SINGLE}
    +function FMod(const a, b: Single): Single;inline;overload;
    +begin
    +  result:= a-b * trunc(a/b);
    +end;
    +{$endif FPC_HAS_TYPE_SINGLE}
     
    +{$ifdef FPC_HAS_TYPE_DOUBLE}
    +function FMod(const a, b: Double): Double;inline;overload;
    +begin
    +  result:= a-b * trunc(a/b);
    +end;
    +{$endif FPC_HAS_TYPE_DOUBLE}
    +
    +{$ifdef FPC_HAS_TYPE_EXTENDED}
    +function FMod(const a, b: Extended): Extended;inline;overload;
    +begin
    +  result:= a-b * trunc(a/b);
    +end;
    +{$endif FPC_HAS_TYPE_EXTENDED}
    +
    +operator mod(const a,b:float) c:float;inline;
    +begin
    +  c:= a-b * trunc(a/b);  
    +end;
    +
     function ifthen(val:boolean;const iftrue:integer; const iffalse:integer= 0) :integer;
     begin
       if val then result:=iftrue else result:=iffalse;
    
    fmodpatch.patch (1,654 bytes)

Activities

Thaddy de Koning

2016-10-15 08:48

reporter   ~0095165

Last edited: 2016-10-15 10:48

View 7 revisions

I didn't provide a patch because I was not sure about code ordering in math.
The provided code is tested against C fmodf and compatible, but I mistakenly copied the wrong code: floor should be trunc to give consistent values for negative values of a or b.

Correct version:

 operator mod(const a,b:float) c:float;inline;
  begin
    c:= a-b * trunc(a/b);
  end;
-- c test
#include <stdio.h>
#include <math.h>
int main()
{
  printf("%f\n", fmodf(18.5f, 4.2f));
  printf("%f\n", fmodf(-18.5f, 4.2f));
  printf("%f\n", fmodf(18.5f, -4.2f));
}
-- fpc test

program fmod;
uses math; //locally patched
begin
  writeln( 18.5 mod 4.2 :4:8);
  writeln( -18.5 mod 4.2 :4:8);
  writeln( 18.5 mod -4.2 :4:8);
end.

Java uses the mod operator on floats too:
public class Fmodj{
    public static void main(String[] args){
            System.out.println( 18.5f % 4.2f );
            System.out.println( -18.5f % 4.2f );
            System.out.println( 18.5f % -4.2f );
    }
}

Note in java it is part of System. That can also be done for FPC?

Marco van de Voort

2016-10-15 13:49

manager   ~0095172

Delphi uses a function: http://docwiki.embarcadero.com/Libraries/Berlin/de/System.Math.FMod

Thaddy de Koning

2016-10-15 14:25

reporter   ~0095174

Last edited: 2016-10-15 16:34

View 14 revisions

Marco, Don't spoil my weekend ;)
Implement both, then. objfpc mod operator | delphi function. deprecated.

As long as it is implemented.
I have a strong favor for the operator since it seems natural and we can do it. Delphi probably will at some point anyway.

BTW: in this case my code is guaranteed clean roomed.

Why not have both? A working mod operator (everywhere) and fmod functions for D comp.?

At the least it is missing core functionality and FPC can solve it in a more elegant way than Delphi can. The body for operator or function is the same code anyway.

I hope it is resolved like:
Oh, well just:

program modplay;
{$ifdef fpc}{$mode objfpc}{$endif}
uses math;
operator mod(const a,b:float) c:float;inline;
begin
  c:= a-b * trunc(a/b);
end;

function fmod(const a, b: float): float;inline;deprecated 'use mod, unless your code needs to work in Delphi';
begin
  result := a mod b;
end;
begin
end.

That is Delphi compatible for the function. That Delphi overloads this function is a bit pointless for mod.

Thaddy de Koning

2016-11-19 12:43

reporter   ~0096030

Last edited: 2016-11-19 12:44

View 2 revisions

Any news? Otherwise I will implement the operator and a function, with permission. And provide a patch.

This is really an omission in basic functionality,

Michael Van Canneyt

2016-11-19 13:26

administrator   ~0096034

If you have a patch that implements both, I will apply it.
I would not add the deprecated though.

Sergei Gorelkin

2016-11-19 14:52

developer   ~0096045

The purpose of fmod function is to calculate remainder from division of any arguments, including those which overflow or lose precision when directly divided. The proposed one-line implementation works only with very limited range of arguments.
Delphi overloads this function for different floating-point types in order to improve speed and avoid unnecessary conversions, just like other basic math is done without precision promotions.

Thaddy de Koning

2016-11-19 16:11

reporter   ~0096047

Sergei,

Current FPC practice in the math unit is that it chooses maximum precision (just look at the available operator overloads) but I will do my utmost to provide some others with lesser precision as well.

Thaddy de Koning

2016-11-20 09:04

reporter  

fmodtest.pp (986 bytes)
program fmodtest;
{$mode objfpc}
uses math;
var
&as,bs:single;
ad,bd:double;
ae,be:extended;

begin
&as:=18.5;ad:=18.5;ae:=18.5;
bs:=4.2;bd:=4.2;be:=4.2;
// simple modulo
writeln(&as mod bs :4:8);
writeln(ad mod bd :4:8);
writeln(ae mod be :4:8);

// single
writeln(fmod(&as,bs) :4:8);
// double
writeln(fmod(ad,bd) :4:8);
//Extended
writeln(fmod(ae,be) :4:8);

// numerator sign changed. Sign should be numerator sign
&as:=-18.5;ad:=-18.5;ae:=-18.5;
// simple modulo
writeln(&as mod bs :4:8);
writeln(ad mod bd :4:8);
writeln(ae mod be :4:8);

// single
writeln(fmod(&as,bs) :4:8);
// double
writeln(fmod(ad,bd) :4:8);
//Extended
writeln(fmod(ae,be) :4:8);

// denominator sign changed. Sign should be numerator sign
&as:=18.5;ad:=18.5;ae:=18.5;
bs:=-4.2;bd:=-4.2;be:=-4.2;
// simple modulo
writeln(&as mod bs :4:8);
writeln(ad mod bd :4:8);
writeln(ae mod be :4:8);
// single
writeln(fmod(&as,bs) :4:8);
// double
writeln(fmod(ad,bd) :4:8);
//Extended
writeln(fmod(ae,be) :4:8);
end.
fmodtest.pp (986 bytes)

Thaddy de Koning

2016-11-20 09:05

reporter  

fmodpatch.patch (1,654 bytes)
Index: math.pp
===================================================================
--- math.pp	(revision 34924)
+++ math.pp	(working copy)
@@ -192,6 +192,21 @@
 procedure DivMod(Dividend: DWord; Divisor: DWord; var Result, Remainder: DWord);
 procedure DivMod(Dividend: LongInt; Divisor: LongInt; var Result, Remainder: LongInt);
 
+{ Floating point modulo}
+{$ifdef FPC_HAS_TYPE_SINGLE}
+function FMod(const a, b: Single): Single;inline;overload;
+{$endif FPC_HAS_TYPE_SINGLE}
+
+{$ifdef FPC_HAS_TYPE_DOUBLE}
+function FMod(const a, b: Double): Double;inline;overload;
+{$endif FPC_HAS_TYPE_DOUBLE}
+
+{$ifdef FPC_HAS_TYPE_EXTENDED}
+function FMod(const a, b: Extended): Extended;inline;overload;
+{$endif FPC_HAS_TYPE_EXTENDED}
+
+operator mod(const a,b:float) c:float;inline;
+
 // Sign functions
 Type
   TValueSign = -1..1;
@@ -2380,7 +2395,33 @@
 end;
 {$endif FPC_MATH_HAS_DIVMOD}
 
+{ Floating point modulo}
+{$ifdef FPC_HAS_TYPE_SINGLE}
+function FMod(const a, b: Single): Single;inline;overload;
+begin
+  result:= a-b * trunc(a/b);
+end;
+{$endif FPC_HAS_TYPE_SINGLE}
 
+{$ifdef FPC_HAS_TYPE_DOUBLE}
+function FMod(const a, b: Double): Double;inline;overload;
+begin
+  result:= a-b * trunc(a/b);
+end;
+{$endif FPC_HAS_TYPE_DOUBLE}
+
+{$ifdef FPC_HAS_TYPE_EXTENDED}
+function FMod(const a, b: Extended): Extended;inline;overload;
+begin
+  result:= a-b * trunc(a/b);
+end;
+{$endif FPC_HAS_TYPE_EXTENDED}
+
+operator mod(const a,b:float) c:float;inline;
+begin
+  c:= a-b * trunc(a/b);  
+end;
+
 function ifthen(val:boolean;const iftrue:integer; const iffalse:integer= 0) :integer;
 begin
   if val then result:=iftrue else result:=iffalse;
fmodpatch.patch (1,654 bytes)

Thaddy de Koning

2016-11-20 09:06

reporter   ~0096069

Last edited: 2016-11-20 13:23

View 7 revisions

Patch attached. Test attached.

- overloaded functions for fmod, delphi compatible, inlined.
- overloaded operator mod for floating point like in languages like java etc. Chooses highest precision available conform the math.pp conventions used for operator overloads.

The functions and operator are also tested against GNU C 4.9.2 and Java and render the same results.

All functions are taken from the original postings, based on the mod overload and developed on linux. Guaranteed clean-roomed. (I am still stuck at RPi3 as main machine due to bet)
The function prototypes to be delphi compatible were developed first and compared to delphi docs afterwards. I removed some border cases (e.g. int mod floats).

Thaddy de Koning

2016-11-20 09:39

reporter   ~0096071

Michael, if accepted, will you be so kind to update the docs? I am a complete noob in patching and building docs,so I'd rather leave that to you.

Michael Van Canneyt

2016-11-26 17:58

administrator   ~0096260

Applied the patch, thank you.

Issue History

Date Modified Username Field Change
2016-10-15 08:43 Thaddy de Koning New Issue
2016-10-15 08:48 Thaddy de Koning Note Added: 0095165
2016-10-15 08:49 Thaddy de Koning Note Edited: 0095165 View Revisions
2016-10-15 09:11 Thaddy de Koning Note Edited: 0095165 View Revisions
2016-10-15 09:18 Thaddy de Koning Note Edited: 0095165 View Revisions
2016-10-15 09:20 Thaddy de Koning Note Edited: 0095165 View Revisions
2016-10-15 10:40 Thaddy de Koning Note Edited: 0095165 View Revisions
2016-10-15 10:48 Thaddy de Koning Note Edited: 0095165 View Revisions
2016-10-15 13:49 Marco van de Voort Note Added: 0095172
2016-10-15 14:25 Thaddy de Koning Note Added: 0095174
2016-10-15 14:27 Thaddy de Koning Note Edited: 0095174 View Revisions
2016-10-15 14:28 Thaddy de Koning Note Edited: 0095174 View Revisions
2016-10-15 14:29 Thaddy de Koning Note Edited: 0095174 View Revisions
2016-10-15 14:32 Thaddy de Koning Note Edited: 0095174 View Revisions
2016-10-15 14:33 Thaddy de Koning Note Edited: 0095174 View Revisions
2016-10-15 14:41 Thaddy de Koning Note Edited: 0095174 View Revisions
2016-10-15 14:52 Thaddy de Koning Note Edited: 0095174 View Revisions
2016-10-15 14:52 Thaddy de Koning Note Edited: 0095174 View Revisions
2016-10-15 14:55 Thaddy de Koning Note Edited: 0095174 View Revisions
2016-10-15 16:31 Thaddy de Koning Note Edited: 0095174 View Revisions
2016-10-15 16:32 Thaddy de Koning Note Edited: 0095174 View Revisions
2016-10-15 16:34 Thaddy de Koning Note Edited: 0095174 View Revisions
2016-10-15 16:34 Thaddy de Koning Note Edited: 0095174 View Revisions
2016-11-19 12:43 Thaddy de Koning Note Added: 0096030
2016-11-19 12:44 Thaddy de Koning Note Edited: 0096030 View Revisions
2016-11-19 13:24 Michael Van Canneyt Assigned To => Michael Van Canneyt
2016-11-19 13:24 Michael Van Canneyt Status new => assigned
2016-11-19 13:26 Michael Van Canneyt Note Added: 0096034
2016-11-19 14:52 Sergei Gorelkin Note Added: 0096045
2016-11-19 16:11 Thaddy de Koning Note Added: 0096047
2016-11-20 09:04 Thaddy de Koning File Added: fmodtest.pp
2016-11-20 09:05 Thaddy de Koning File Added: fmodpatch.patch
2016-11-20 09:06 Thaddy de Koning Note Added: 0096069
2016-11-20 09:07 Thaddy de Koning Note Edited: 0096069 View Revisions
2016-11-20 09:11 Thaddy de Koning Note Edited: 0096069 View Revisions
2016-11-20 09:39 Thaddy de Koning Note Added: 0096071
2016-11-20 13:20 Thaddy de Koning Note Edited: 0096069 View Revisions
2016-11-20 13:22 Thaddy de Koning Note Edited: 0096069 View Revisions
2016-11-20 13:23 Thaddy de Koning Note Edited: 0096069 View Revisions
2016-11-20 13:23 Thaddy de Koning Note Edited: 0096069 View Revisions
2016-11-26 17:58 Michael Van Canneyt Fixed in Revision => 34968
2016-11-26 17:58 Michael Van Canneyt Note Added: 0096260
2016-11-26 17:58 Michael Van Canneyt Status assigned => resolved
2016-11-26 17:58 Michael Van Canneyt Fixed in Version => 3.1.1
2016-11-26 17:58 Michael Van Canneyt Resolution open => fixed
2016-11-26 17:58 Michael Van Canneyt Target Version => 3.2.0