Extended math RTL

This commit is contained in:
Michael Schimmel
2026-01-13 18:08:52 +01:00
parent 1c6a6fe5a0
commit 629dbc97ca
6 changed files with 457 additions and 90 deletions
+41 -36
View File
@@ -14,9 +14,17 @@ type
public
// --- Constants ---
[TRtlConst('false')]
[AstDoc('Boolean false.')]
class function CFalse: Boolean; static;
[TRtlConst('true')]
[AstDoc('Boolean true.')]
class function CTrue: Boolean; static;
[TRtlConst('NaN')]
[AstDoc('Not a Number (IEEE 754).')]
class function Const_NaN: Double; static;
class function CNaN: Double; static;
// --- Arithmetic Operators ---
@@ -38,17 +46,17 @@ type
class function Multiply(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('/', Pure)]
[AstDoc('Divides A by B. Always returns a floating point number.')]
[AstDoc('Divides A by B. Returns NaN on division by zero.')]
[AstSignature('(number, number) -> number')]
class function Divide(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('div', Pure)]
[AstDoc('Performs integer division.')]
[AstDoc('Performs integer division. Returns NaN on division by zero.')]
[AstSignature('(number, number) -> number')]
class function IntDivide(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('mod', Pure)]
[AstDoc('Returns the remainder of integer division.')]
[AstDoc('Returns the remainder of integer division. Returns NaN on division by zero.')]
[AstSignature('(number, number) -> number')]
class function Modulus(const Args: TArray<TDataValue>): TDataValue; static;
@@ -160,11 +168,6 @@ type
[TRtlExport('/', Pure)]
class function Divide_Float_Ordinal_Float(A: Double; B: Int64): Double; static;
[TRtlExport('div', Pure)]
class function Div_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64; static;
[TRtlExport('mod', Pure)]
class function Mod_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64; static;
[TRtlExport('=', Pure)]
class function Equal_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64; static;
[TRtlExport('=', Pure)]
@@ -246,7 +249,7 @@ implementation
{ TRtlCoreFunctions - Constants }
class function TRtlCoreFunctions.Const_NaN: Double;
class function TRtlCoreFunctions.CNaN: Double;
begin
Result := System.Math.NaN;
end;
@@ -284,6 +287,7 @@ class function TRtlCoreFunctions.Divide(const Args: TArray<TDataValue>): TDataVa
begin
if Length(Args) <> 2 then
raise EArgumentException.Create('Operator / requires 2 arguments.');
Result := Args[0].AsScalar / Args[1].AsScalar;
end;
@@ -291,6 +295,7 @@ class function TRtlCoreFunctions.IntDivide(const Args: TArray<TDataValue>): TDat
begin
if Length(Args) <> 2 then
raise EArgumentException.Create('Operator div requires 2 arguments.');
Result := Args[0].AsScalar div Args[1].AsScalar;
end;
@@ -298,7 +303,8 @@ class function TRtlCoreFunctions.Modulus(const Args: TArray<TDataValue>): TDataV
begin
if Length(Args) <> 2 then
raise EArgumentException.Create('Operator mod requires 2 arguments.');
Result := Args[0].AsScalar mod Args[1].AsScalar;
Result := TScalar.FromInt64(Int64(Args[0].AsScalar) mod Args[1].AsScalar);
end;
class function TRtlCoreFunctions.Equal(const Args: TArray<TDataValue>): TDataValue;
@@ -447,42 +453,30 @@ begin
Result := A * B;
end;
// Divide
// Divide - Updated to return NaN on div0
class function TRtlCoreFunctions.Divide_Ordinal_Ordinal_Float(A, B: Int64): Double;
begin
if B = 0 then
raise EDivByZero.Create('Division by zero.');
Result := A / B;
end;
class function TRtlCoreFunctions.Divide_Float_Float_Float(A, B: Double): Double;
begin
if B = 0.0 then
raise EDivByZero.Create('Division by zero.');
Result := A / B;
Result := NaN
else
Result := A / B;
end;
class function TRtlCoreFunctions.Divide_Ordinal_Float_Float(A: Int64; B: Double): Double;
begin
if B = 0.0 then
raise EDivByZero.Create('Division by zero.');
Result := A / B;
Result := NaN
else
Result := A / B;
end;
class function TRtlCoreFunctions.Divide_Float_Ordinal_Float(A: Double; B: Int64): Double;
begin
if B = 0 then
raise EDivByZero.Create('Division by zero.');
Result := A / B;
end;
// Integer Math
class function TRtlCoreFunctions.Div_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64;
begin
Result := A div B;
end;
class function TRtlCoreFunctions.Mod_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64;
begin
Result := A mod B;
end;
// Comparisons
class function TRtlCoreFunctions.Equal_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64;
begin
@@ -490,15 +484,15 @@ begin
end;
class function TRtlCoreFunctions.Equal_Float_Float_Ordinal(A, B: Double): Int64;
begin
Result := Ord(SameValue(A, B));
Result := Ord(A = B);
end;
class function TRtlCoreFunctions.Equal_Ordinal_Float_Ordinal(A: Int64; B: Double): Int64;
begin
Result := Ord(SameValue(A, B));
Result := Ord(A = B);
end;
class function TRtlCoreFunctions.Equal_Float_Ordinal_Ordinal(A: Double; B: Int64): Int64;
begin
Result := Ord(SameValue(A, B));
Result := Ord(A = B);
end;
class function TRtlCoreFunctions.Equal_Keyword_Keyword_Ordinal(A, B: Int64): Int64;
begin
@@ -511,15 +505,15 @@ begin
end;
class function TRtlCoreFunctions.NotEqual_Float_Float_Ordinal(A, B: Double): Int64;
begin
Result := Ord(not SameValue(A, B));
Result := Ord(A <> B);
end;
class function TRtlCoreFunctions.NotEqual_Ordinal_Float_Ordinal(A: Int64; B: Double): Int64;
begin
Result := Ord(not SameValue(A, B));
Result := Ord(A <> B);
end;
class function TRtlCoreFunctions.NotEqual_Float_Ordinal_Ordinal(A: Double; B: Int64): Int64;
begin
Result := Ord(not SameValue(A, B));
Result := Ord(A <> B);
end;
class function TRtlCoreFunctions.NotEqual_Keyword_Keyword_Ordinal(A, B: Int64): Int64;
begin
@@ -599,6 +593,17 @@ class function TRtlCoreFunctions.And_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64
begin
Result := A and B;
end;
class function TRtlCoreFunctions.CFalse: Boolean;
begin
Result := false;
end;
class function TRtlCoreFunctions.CTrue: Boolean;
begin
Result := true;
end;
class function TRtlCoreFunctions.Or_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64;
begin
Result := A or B;
+325 -1
View File
@@ -12,7 +12,17 @@ uses
type
TRtlMathFunctions = record
public
// --- Math Functions ---
// --- Constants ---
[TRtlConst('Pi')]
[AstDoc('Pi.')]
class function CPi: Double; static;
[TRtlConst('e')]
[AstDoc('Euler''s number.')]
class function CE: Double; static;
// --- Basic Math ---
[TRtlExport('abs', Pure)]
[AstDoc('Returns the absolute value of a number.')]
@@ -44,6 +54,16 @@ type
[AstSignature('(number) -> number')]
class function Sign(const Arg: TScalar): TScalar; static;
[TRtlExport('min', Pure)]
[AstDoc('Returns the smaller of two numbers.')]
[AstSignature('(number, number) -> number')]
class function Min(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('max', Pure)]
[AstDoc('Returns the larger of two numbers.')]
[AstSignature('(number, number) -> number')]
class function Max(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('sqrt', Pure)]
[AstDoc('Returns the square root of a number.')]
[AstSignature('(number) -> number')]
@@ -54,12 +74,98 @@ type
[AstSignature('(number, number) -> number')]
class function Pow(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('exp', Pure)]
[AstDoc('Returns e raised to the power of X.')]
[AstSignature('(number) -> number')]
class function Exp(const Arg: TScalar): TScalar; static;
[TRtlExport('log', Pure)]
[AstDoc('Returns the natural logarithm (Base e).')]
[AstSignature('(number) -> number')]
class function Log(const Arg: TScalar): TScalar; static;
[TRtlExport('log10', Pure)]
[AstDoc('Returns the base-10 logarithm.')]
[AstSignature('(number) -> number')]
class function Log10(const Arg: TScalar): TScalar; static;
// --- Trigonometry ---
[TRtlExport('sin', Pure)]
[AstDoc('Returns the sine of the angle (in radians).')]
[AstSignature('(number) -> number')]
class function Sin(const Arg: TScalar): TScalar; static;
[TRtlExport('cos', Pure)]
[AstDoc('Returns the cosine of the angle (in radians).')]
[AstSignature('(number) -> number')]
class function Cos(const Arg: TScalar): TScalar; static;
[TRtlExport('tan', Pure)]
[AstDoc('Returns the tangent of the angle (in radians).')]
[AstSignature('(number) -> number')]
class function Tan(const Arg: TScalar): TScalar; static;
[TRtlExport('asin', Pure)]
[AstDoc('Returns the arc sine.')]
[AstSignature('(number) -> number')]
class function ArcSin(const Arg: TScalar): TScalar; static;
[TRtlExport('acos', Pure)]
[AstDoc('Returns the arc cosine.')]
[AstSignature('(number) -> number')]
class function ArcCos(const Arg: TScalar): TScalar; static;
[TRtlExport('atan', Pure)]
[AstDoc('Returns the arc tangent.')]
[AstSignature('(number) -> number')]
class function ArcTan(const Arg: TScalar): TScalar; static;
[TRtlExport('atan2', Pure)]
[AstDoc('Returns the angle whose tangent is Y / X.')]
[AstSignature('(y, x) -> number')]
class function ArcTan2(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('sinh', Pure)]
[AstDoc('Returns the hyperbolic sine.')]
[AstSignature('(number) -> number')]
class function Sinh(const Arg: TScalar): TScalar; static;
[TRtlExport('cosh', Pure)]
[AstDoc('Returns the hyperbolic cosine.')]
[AstSignature('(number) -> number')]
class function Cosh(const Arg: TScalar): TScalar; static;
[TRtlExport('tanh', Pure)]
[AstDoc('Returns the hyperbolic tangent.')]
[AstSignature('(number) -> number')]
class function Tanh(const Arg: TScalar): TScalar; static;
// --- Conversion ---
[TRtlExport('deg2rad', Pure)]
[AstDoc('Converts degrees to radians.')]
[AstSignature('(number) -> number')]
class function DegToRad(const Arg: TScalar): TScalar; static;
[TRtlExport('rad2deg', Pure)]
[AstDoc('Converts radians to degrees.')]
[AstSignature('(number) -> number')]
class function RadToDeg(const Arg: TScalar): TScalar; static;
// --- Utils ---
[TRtlExport('random', Impure)]
[AstDoc('Returns a random number. No args: [0..1). Arg N: Integer [0..N).')]
[AstSignature('() -> float')]
[AstSignature('(number) -> number')]
class function Random(const Args: TArray<TDataValue>): TDataValue; static;
[TRtlExport('is-NaN', Pure)]
[AstDoc('Returns true if the value is Not-a-Number (NaN).')]
[AstSignature('(number) -> boolean')]
class function IsNaN(const Arg: TScalar): TScalar; static;
// --- Static Specializations ---
[TRtlExport('abs', Pure)]
@@ -91,16 +197,57 @@ type
[TRtlExport('pow', Pure)]
class function Pow_Float_Ordinal_Float(A: Double; B: Int64): Double; static;
[TRtlExport('min', Pure)]
class function Min_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64; static;
[TRtlExport('min', Pure)]
class function Min_Float_Float_Float(A, B: Double): Double; static;
[TRtlExport('max', Pure)]
class function Max_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64; static;
[TRtlExport('max', Pure)]
class function Max_Float_Float_Float(A, B: Double): Double; static;
[TRtlExport('exp', Pure)]
class function Exp_Float_Float(A: Double): Double; static;
[TRtlExport('log', Pure)]
class function Log_Float_Float(A: Double): Double; static;
[TRtlExport('log10', Pure)]
class function Log10_Float_Float(A: Double): Double; static;
[TRtlExport('sin', Pure)]
class function Sin_Float_Float(A: Double): Double; static;
[TRtlExport('cos', Pure)]
class function Cos_Float_Float(A: Double): Double; static;
[TRtlExport('tan', Pure)]
class function Tan_Float_Float(A: Double): Double; static;
[TRtlExport('random', Impure)]
class function Random_Void_Float: Double; static;
[TRtlExport('random', Impure)]
class function Random_Ordinal_Ordinal(Limit: Int64): Int64; static;
[TRtlExport('is-NaN', Pure)]
class function IsNaN_Float_Ordinal(A: Double): Int64; static;
end;
implementation
{ TRtlMathFunctions }
// --- Constants ---
class function TRtlMathFunctions.CPi: Double;
begin
Result := TScalar.FromDouble(System.Pi);
end;
class function TRtlMathFunctions.CE: Double;
begin
Result := TScalar.FromDouble(2.71828182845904523536);
end;
// --- Basic Math ---
class function TRtlMathFunctions.Abs(const Arg: TScalar): TScalar;
begin
case Arg.Kind of
@@ -169,6 +316,34 @@ begin
end;
end;
class function TRtlMathFunctions.Min(const Args: TArray<TDataValue>): TDataValue;
begin
if Length(Args) <> 2 then
raise EArgumentException.Create('Min requires 2 arguments.');
var a: TScalar := Args[0].AsScalar;
var b: TScalar := Args[1].AsScalar;
if (a.Kind = TScalar.TKind.Ordinal) and (b.Kind = TScalar.TKind.Ordinal) then
Result := TScalar.FromInt64(System.Math.Min(a.Value.AsInt64, b.Value.AsInt64))
else
Result := TScalar.FromDouble(System.Math.Min(Double(a), Double(b)));
end;
class function TRtlMathFunctions.Max(const Args: TArray<TDataValue>): TDataValue;
begin
if Length(Args) <> 2 then
raise EArgumentException.Create('Max requires 2 arguments.');
var a: TScalar := Args[0].AsScalar;
var b: TScalar := Args[1].AsScalar;
if (a.Kind = TScalar.TKind.Ordinal) and (b.Kind = TScalar.TKind.Ordinal) then
Result := TScalar.FromInt64(System.Math.Max(a.Value.AsInt64, b.Value.AsInt64))
else
Result := TScalar.FromDouble(System.Math.Max(Double(a), Double(b)));
end;
class function TRtlMathFunctions.Sqrt(const Arg: TScalar): TScalar;
begin
var val: Double := Arg; // Implicit cast handles Ordinal and Float
@@ -185,6 +360,92 @@ begin
Result := TScalar.FromDouble(System.Math.Power(baseVal, expVal));
end;
class function TRtlMathFunctions.Exp(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Exp(Double(Arg)));
end;
class function TRtlMathFunctions.Log(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Ln(Double(Arg)));
end;
class function TRtlMathFunctions.Log10(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.Log10(Double(Arg)));
end;
// --- Trigonometry ---
class function TRtlMathFunctions.Sin(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Sin(Double(Arg)));
end;
class function TRtlMathFunctions.Cos(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Cos(Double(Arg)));
end;
class function TRtlMathFunctions.Tan(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.Tan(Double(Arg)));
end;
class function TRtlMathFunctions.ArcSin(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.ArcSin(Double(Arg)));
end;
class function TRtlMathFunctions.ArcCos(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.ArcCos(Double(Arg)));
end;
class function TRtlMathFunctions.ArcTan(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.ArcTan(Double(Arg)));
end;
class function TRtlMathFunctions.ArcTan2(const Args: TArray<TDataValue>): TDataValue;
begin
if Length(Args) <> 2 then
raise EArgumentException.Create('Atan2 requires 2 arguments (Y, X).');
var y: Double := Args[0].AsScalar;
var x: Double := Args[1].AsScalar;
Result := TScalar.FromDouble(System.Math.ArcTan2(y, x));
end;
class function TRtlMathFunctions.Sinh(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.Sinh(Double(Arg)));
end;
class function TRtlMathFunctions.Cosh(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.Cosh(Double(Arg)));
end;
class function TRtlMathFunctions.Tanh(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.Tanh(Double(Arg)));
end;
// --- Conversion ---
class function TRtlMathFunctions.DegToRad(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.DegToRad(Double(Arg)));
end;
class function TRtlMathFunctions.RadToDeg(const Arg: TScalar): TScalar;
begin
Result := TScalar.FromDouble(System.Math.RadToDeg(Double(Arg)));
end;
// --- Utils ---
class function TRtlMathFunctions.Random(const Args: TArray<TDataValue>): TDataValue;
begin
if Length(Args) = 0 then
@@ -195,6 +456,14 @@ begin
raise EArgumentException.Create('Random expects 0 arguments or 1 integer argument.');
end;
class function TRtlMathFunctions.IsNaN(const Arg: TScalar): TScalar;
begin
if (Arg.Kind = TScalar.TKind.Float) and Arg.Value.AsDouble.IsNaN then
Result := TScalar.FromBoolean(True)
else
Result := TScalar.FromBoolean(False);
end;
// --- Static Specializations ---
class function TRtlMathFunctions.Abs_Ordinal_Ordinal(A: Int64): Int64;
@@ -257,6 +526,56 @@ begin
Result := System.Math.Power(A, B);
end;
class function TRtlMathFunctions.Min_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64;
begin
Result := System.Math.Min(A, B);
end;
class function TRtlMathFunctions.Min_Float_Float_Float(A, B: Double): Double;
begin
Result := System.Math.Min(A, B);
end;
class function TRtlMathFunctions.Max_Ordinal_Ordinal_Ordinal(A, B: Int64): Int64;
begin
Result := System.Math.Max(A, B);
end;
class function TRtlMathFunctions.Max_Float_Float_Float(A, B: Double): Double;
begin
Result := System.Math.Max(A, B);
end;
class function TRtlMathFunctions.Exp_Float_Float(A: Double): Double;
begin
Result := System.Exp(A);
end;
class function TRtlMathFunctions.Log_Float_Float(A: Double): Double;
begin
Result := System.Ln(A);
end;
class function TRtlMathFunctions.Log10_Float_Float(A: Double): Double;
begin
Result := System.Math.Log10(A);
end;
class function TRtlMathFunctions.Sin_Float_Float(A: Double): Double;
begin
Result := System.Sin(A);
end;
class function TRtlMathFunctions.Cos_Float_Float(A: Double): Double;
begin
Result := System.Cos(A);
end;
class function TRtlMathFunctions.Tan_Float_Float(A: Double): Double;
begin
Result := System.Math.Tan(A);
end;
class function TRtlMathFunctions.Random_Void_Float: Double;
begin
Result := System.Random;
@@ -267,4 +586,9 @@ begin
Result := System.Random(Limit);
end;
class function TRtlMathFunctions.IsNaN_Float_Ordinal(A: Double): Int64;
begin
Result := Ord(A.IsNaN);
end;
end.
-3
View File
@@ -783,9 +783,6 @@ var
staticType: IStaticType;
pair: TPair<string, TRtlFunctionInfo>;
begin
AScope.Define('true', TDataValue(TScalar.FromBoolean(True)), TTypes.Boolean, 'Boolean true.');
AScope.Define('false', TDataValue(TScalar.FromBoolean(False)), TTypes.Boolean, 'Boolean false.');
for pair in FStaticFuncMap do
begin
rtlName := pair.Key;
+11 -6
View File
@@ -620,12 +620,17 @@ begin
if (A.Kind in [TKind.DateTime, TKind.Keyword, TKind.Boolean]) or (B.Kind in [TKind.DateTime, TKind.Keyword, TKind.Boolean]) then
raise EArgumentException.CreateFmt('Operator Divide not supported for types %s and %s', [A.Kind.ToString, B.Kind.ToString]);
var valB: Double := B;
if valB = 0.0 then
raise EDivByZero.Create('Division by zero');
var valA: Double := A;
Result := valA / valB;
if B.Kind in [TKind.Float, TKind.DateTime] then
begin
Result := A.Value.AsDouble / B.Value.AsDouble;
end
else
begin
var valB: Int64 := B;
if valB = 0 then
raise EDivByZero.Create('Division by zero');
Result := A.Value.AsDouble / valB;
end;
end;
class operator TScalar.IntDivide(const A, B: TScalar): TScalar;