blob: a6cf2a1ca0be421721d0dd691d45681fa24e0bf3 [file] [log] [blame]
------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- S Y S T E M . D O U B L E _ R E A L --
-- --
-- B o d y --
-- --
-- Copyright (C) 2021-2022, Free Software Foundation, Inc. --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 3, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
-- GNAT was originally developed by the GNAT team at New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc. --
-- --
------------------------------------------------------------------------------
package body System.Double_Real is
function Is_NaN (N : Num) return Boolean is (N /= N);
-- Return True if N is a NaN
function Is_Infinity (N : Num) return Boolean is (Is_NaN (N - N));
-- Return True if N is an infinity. Used to avoid propagating meaningless
-- errors when the result of a product is an infinity.
function Is_Zero (N : Num) return Boolean is (N = -N);
-- Return True if N is a Zero. Used to preserve the sign when the result of
-- a product is a zero.
package Product is
function Two_Prod (A, B : Num) return Double_T;
function Two_Sqr (A : Num) return Double_T;
end Product;
-- The low-level implementation of multiplicative operations
package body Product is separate;
-- This is a separate body because the implementation depends on whether a
-- Fused Multiply-Add instruction is available on the target.
-------------------
-- Quick_Two_Sum --
-------------------
function Quick_Two_Sum (A, B : Num) return Double_T is
S : constant Num := A + B;
V : constant Num := S - A;
E : constant Num := B - V;
begin
return (S, E);
end Quick_Two_Sum;
-------------
-- Two_Sum --
-------------
function Two_Sum (A, B : Num) return Double_T is
S : constant Num := A + B;
V : constant Num := S - A;
E : constant Num := (A - (S - V)) + (B - V);
begin
return (S, E);
end Two_Sum;
--------------
-- Two_Diff --
--------------
function Two_Diff (A, B : Num) return Double_T is
S : constant Num := A - B;
V : constant Num := S - A;
E : constant Num := (A - (S - V)) - (B + V);
begin
return (S, E);
end Two_Diff;
--------------
-- Two_Prod --
--------------
function Two_Prod (A, B : Num) return Double_T renames Product.Two_Prod;
-------------
-- Two_Sqr --
-------------
function Two_Sqr (A : Num) return Double_T renames Product.Two_Sqr;
---------
-- "+" --
---------
function "+" (A : Double_T; B : Num) return Double_T is
S : constant Double_T := Two_Sum (A.Hi, B);
begin
return Quick_Two_Sum (S.Hi, S.Lo + A.Lo);
end "+";
function "+" (A, B : Double_T) return Double_T is
S1 : constant Double_T := Two_Sum (A.Hi, B.Hi);
S2 : constant Double_T := Two_Sum (A.Lo, B.Lo);
S3 : constant Double_T := Quick_Two_Sum (S1.Hi, S1.Lo + S2.Hi);
begin
return Quick_Two_Sum (S3.Hi, S3.Lo + S2.Lo);
end "+";
---------
-- "-" --
---------
function "-" (A : Double_T; B : Num) return Double_T is
D : constant Double_T := Two_Diff (A.Hi, B);
begin
return Quick_Two_Sum (D.Hi, D.Lo + A.Lo);
end "-";
function "-" (A, B : Double_T) return Double_T is
D1 : constant Double_T := Two_Diff (A.Hi, B.Hi);
D2 : constant Double_T := Two_Diff (A.Lo, B.Lo);
D3 : constant Double_T := Quick_Two_Sum (D1.Hi, D1.Lo + D2.Hi);
begin
return Quick_Two_Sum (D3.Hi, D3.Lo + D2.Lo);
end "-";
---------
-- "*" --
---------
function "*" (A : Double_T; B : Num) return Double_T is
P : constant Double_T := Two_Prod (A.Hi, B);
begin
if Is_Infinity (P.Hi) or else Is_Zero (P.Hi) then
return (P.Hi, 0.0);
else
return Quick_Two_Sum (P.Hi, P.Lo + A.Lo * B);
end if;
end "*";
function "*" (A, B : Double_T) return Double_T is
P : constant Double_T := Two_Prod (A.Hi, B.Hi);
begin
if Is_Infinity (P.Hi) or else Is_Zero (P.Hi) then
return (P.Hi, 0.0);
else
return Quick_Two_Sum (P.Hi, P.Lo + A.Hi * B.Lo + A.Lo * B.Hi);
end if;
end "*";
---------
-- "/" --
---------
function "/" (A : Double_T; B : Num) return Double_T is
Q1, Q2 : Num;
P, R : Double_T;
begin
Q1 := A.Hi / B;
-- Compute R = A - B * Q1
P := Two_Prod (B, Q1);
R := Two_Diff (A.Hi, P.Hi);
R.Lo := (R.Lo + A.Lo) - P.Lo;
Q2 := (R.Hi + R.Lo) / B;
return Quick_Two_Sum (Q1, Q2);
end "/";
function "/" (A, B : Double_T) return Double_T is
Q1, Q2, Q3 : Num;
R, S : Double_T;
begin
Q1 := A.Hi / B.Hi;
R := A - B * Q1;
Q2 := R.Hi / B.Hi;
R := R - B * Q2;
Q3 := R.Hi / B.Hi;
S := Quick_Two_Sum (Q1, Q2);
return Quick_Two_Sum (S.Hi, S.Lo + Q3);
end "/";
---------
-- Sqr --
---------
function Sqr (A : Double_T) return Double_T is
Q : constant Double_T := Two_Sqr (A.Hi);
begin
if Is_Infinity (Q.Hi) or else Is_Zero (Q.Hi) then
return (Q.Hi, 0.0);
else
return Quick_Two_Sum (Q.Hi, Q.Lo + 2.0 * A.Hi * A.Lo + A.Lo * A.Lo);
end if;
end Sqr;
-------------------
-- From_Unsigned --
-------------------
function From_Unsigned (U : Uns) return Double_T is
begin
return To_Double (Num (U));
end From_Unsigned;
-----------------
-- To_Unsigned --
-----------------
function To_Unsigned (D : Double_T) return Uns is
Hi : constant Num := Num'Truncation (D.Hi);
begin
-- If the high part is already an integer, add Floor of the low part,
-- which means subtract Ceiling of its opposite if it is negative.
if Hi = D.Hi then
if D.Lo < 0.0 then
return Uns (Hi) - Uns (Num'Ceiling (-D.Lo));
else
return Uns (Hi) + Uns (Num'Floor (D.Lo));
end if;
else
return Uns (Hi);
end if;
end To_Unsigned;
end System.Double_Real;