Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unsoundness of 64-bit floating point rounding #111

Closed
svenkeidel opened this issue Jun 18, 2024 · 5 comments · May be fixed by #113
Closed

Unsoundness of 64-bit floating point rounding #111

svenkeidel opened this issue Jun 18, 2024 · 5 comments · May be fixed by #113

Comments

@svenkeidel
Copy link

svenkeidel commented Jun 18, 2024

Hi Antoine,

Thanks for maintaining this library 👍

I get some weird behavior when rounding numbers to double-precision. For example, 0 + -6.561472834256157E307 results in [-132...784,-oo]. I would expect [-6.561472834256157E307, -6.561472834256157E307] since -6.561472834256157E307 is a valid IEEE 64-bit floating point number and does not need to get rounded.

I tried different rounding modes, but the result is the same.

Here is a Java program that reproduces this behavior:

import apron.*;

public class FloatingPointTest {
    public static void main(String args[]) throws ApronException {
        Manager manager = new Polka(true);
        Abstract1 abs = new Abstract1(manager, new Environment());

        System.out.println("real zero:      " + abs.getBound(manager, add(0, -6.561472834256157E307, Texpr1Node.RTYPE_REAL, Texpr1Node.RDIR_ZERO)));

        System.out.println("double nearest: " + abs.getBound(manager, add(0, -6.561472834256157E307, Texpr1Node.RTYPE_DOUBLE, Texpr1Node.RDIR_NEAREST)));
        System.out.println("double zero:    " + abs.getBound(manager, add(0, -6.561472834256157E307, Texpr1Node.RTYPE_DOUBLE, Texpr1Node.RDIR_ZERO)));
        System.out.println("double up:      " + abs.getBound(manager, add(0, -6.561472834256157E307, Texpr1Node.RTYPE_DOUBLE, Texpr1Node.RDIR_UP)));
        System.out.println("double down:    " + abs.getBound(manager, add(0, -6.561472834256157E307, Texpr1Node.RTYPE_DOUBLE, Texpr1Node.RDIR_DOWN)));
        System.out.println("double rnd:     " + abs.getBound(manager, add(0, -6.561472834256157E307, Texpr1Node.RTYPE_DOUBLE, Texpr1Node.RDIR_RND)));
    }

    public static Texpr1Intern add(double x, double y, int rtype, int rdir) {
        return new Texpr1Intern(new Environment(),
                new Texpr1BinNode(
                        Texpr1BinNode.OP_ADD,
                        rtype,
                        rdir,
                        new Texpr1CstNode(new DoubleScalar(x)),
                        new Texpr1CstNode(new DoubleScalar(y))));
    }
}

Output:

real zero:      [-65614728342561566838280620427921665518275175331218847433518129374684930125891058177865074730733075820214710461800638277009131797677509429131210620736536213314089786386935533961751783825182296611718297141065189765439797173860632118472237970876745467949986194082751806201002365069079602145671651872319425478656,-65614728342561566838280620427921665518275175331218847433518129374684930125891058177865074730733075820214710461800638277009131797677509429131210620736536213314089786386935533961751783825182296611718297141065189765439797173860632118472237970876745467949986194082751806201002365069079602145671651872319425478656]

double nearest: [-13280568866681522608140348627917120275996421526520118379319810341978606597044324134108464303574704223726265274302574774897638769117908177675842225719714830713068935818754787764355466920198820482431381415239848077773016801599655207887138886226766750565545726172286052583074150471702405654385833419019397239321799407394881640531814848410820707989157866092958931352583849640704897948594291781117436469345985623228118128757466474102982240945504908729334832527147674090066448319952864154753273414539939422905537013332369137535041508050952522596711122925384909651111883472752138096654050331506230795747232618168994666520875738939395145729/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784,-oo]
double zero:    [-13280568866681522608140348627917120275996421526520118379319810341978606597044324134108464303574704223726265274302574774897638769117908177675842225719714830713068935818754787764355466920198820482431381415239848077773016801599655207887138886226766750565545726172286052583074150471702405654385833419019397239321799407394881640531814848410820707989157866092958931352583849640704897948594291781117436469345985623228118128757466474102982240945504908729334832527147674090066448319952864154753273414539939422905537013332369137535041508050952522596711122925384909651111883472752138096654050331506230795747232618168994666520875738939395145729/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784,-oo]
double up:      [-13280568866681522608140348627917120275996421526520118379319810341978606597044324134108464303574704223726265274302574774897638769117908177675842225719714830713068935818754787764355466920198820482431381415239848077773016801599655207887138886226766750565545726172286052583074150471702405654385833419019397239321799407394881640531814848410820707989157866092958931352583849640704897948594291781117436469345985623228118128757466474102982240945504908729334832527147674090066448319952864154753273414539939422905537013332369137535041508050952522596711122925384909651111883472752138096654050331506230795747232618168994666520875738939395145729/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784,-oo]
double down:    [-13280568866681522608140348627917120275996421526520118379319810341978606597044324134108464303574704223726265274302574774897638769117908177675842225719714830713068935818754787764355466920198820482431381415239848077773016801599655207887138886226766750565545726172286052583074150471702405654385833419019397239321799407394881640531814848410820707989157866092958931352583849640704897948594291781117436469345985623228118128757466474102982240945504908729334832527147674090066448319952864154753273414539939422905537013332369137535041508050952522596711122925384909651111883472752138096654050331506230795747232618168994666520875738939395145729/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784,-oo]
double rnd:     [-13280568866681522608140348627917120275996421526520118379319810341978606597044324134108464303574704223726265274302574774897638769117908177675842225719714830713068935818754787764355466920198820482431381415239848077773016801599655207887138886226766750565545726172286052583074150471702405654385833419019397239321799407394881640531814848410820707989157866092958931352583849640704897948594291781117436469345985623228118128757466474102982240945504908729334832527147674090066448319952864154753273414539939422905537013332369137535041508050952522596711122925384909651111883472752138096654050331506230795747232618168994666520875738939395145729/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784,-oo]
@svenkeidel
Copy link
Author

svenkeidel commented Jun 18, 2024

This only seems to affect Polyhedra.

For Octagons I get the correct result:

real zero:      [-6.561472834256157E307,-6.561472834256157E307]

double nearest: [-6.561472834256157E307,-6.561472834256157E307]
double zero:    [-6.561472834256157E307,-6.561472834256157E307]
double up:      [-6.561472834256157E307,-6.561472834256157E307]
double down:    [-6.561472834256157E307,-6.561472834256157E307]
double rnd:     [-6.561472834256157E307,-6.561472834256157E307]

@antoinemine
Copy link
Owner

Hi.
Thanks for the report!
I think that #113 should fix the issue (tested with OCaml, but should work on all bindings).
Could you confirm it fixes your issue, and please tell me if you spot any regression?

@svenkeidel
Copy link
Author

Thanks for the patch 👍

The issue I posted above seems to be fixed. However, rounding does not seem to round up to infinity.

For example, 1e308 + 1e308 = 2e308 should be rounded with double precision to infinity (maximum double value is 1.7976931348623157e308). However, polyhedra return [2e308, 2e308].

The problem seems to be that polyhedra convert DoubleScalar's to MpqScalar's.
Octagons don't convert the scalars and hence round to infinity as expected.

I refactored the test to test for these cases as well:

import apron.*;

public class FloatingPointTest {
    public static void main(String args[]) throws ApronException {
        test(new Polka(true), 0.0, -6.561472834256157E307d);
        test(new Polka(true), 1E308d, 1E308d);
        test(new Polka(true), -1E308d, -1E308d);
        test(new Polka(true), -Double.MAX_VALUE, -Double.MAX_VALUE);
    }

    public static void test(Manager manager, double x, double y) throws ApronException {
        Abstract1 abs = new Abstract1(manager, new Environment());
        double result = x + y;
        System.out.printf("Test (%s): %e + %e = %e\n", manager.getClass().getSimpleName(), x, y, result);
        contains("double rnd", manager, abs.getBound(manager, add(new DoubleScalar(x), new DoubleScalar(y), Texpr1Node.RTYPE_DOUBLE, Texpr1Node.RDIR_RND)), result);
        System.out.println();
    }

    public static void contains(String message, Manager manager, Interval iv, double result) {
        if(new Interval(new DoubleScalar(result), new DoubleScalar(result)).isLeq(iv)) {
            System.out.printf("Test Succeeded (%s): %s contains %e\n", message, iv, result);
        } else {
            System.out.printf("Test Failed (%s): %s does not contain %e\n", message, iv, result);
        }
    }
    public static Texpr1Intern add(Coeff x, Coeff y, int rtype, int rdir) {
        return new Texpr1Intern(new Environment(),
                new Texpr1BinNode(
                        Texpr1BinNode.OP_ADD,
                        rtype,
                        rdir,
                        new Texpr1CstNode(x),
                        new Texpr1CstNode(y)));
    }
}

Output:

Test (Polka): 0.000000e+00 + -6.561473e+307 = -6.561473e+307
Test Succeeded (double rnd): [-65614728342561566838280620427921665518275175331218847433518129374684930125891058177865074730733075820214710461800638277009131797677509429131210620736536213314089786386935533961751783825182296611718297141065189765439797173860632118472237970876745467949986194082751806201002365069079602145671651872319425478656,-65614728342561566838280620427921665518275175331218847433518129374684930125891058177865074730733075820214710461800638277009131797677509429131210620736536213314089786386935533961751783825182296611718297141065189765439797173860632118472237970876745467949986194082751806201002365069079602145671651872319425478656] contains -6.561473e+307

Test (Polka): 1.000000e+308 + 1.000000e+308 = Infinity
Test Failed (double rnd): [40480450661462115126470838592821451823158814161751640296534110418710808670231638261484039344844229857167741852732082579155921341512552329014647577055830963358087672231341133378641047084323411333096708338731428780909028559232773259433908118852541471468749109324498630538226047471129362902280775393282901974751322333314714885016411885469672543711663110667383208958222093404450203053965993019403300544638051732206717359342392055984223090990920583220905435269993413028784389461212336100655438949972983408852384612782204491640522508493753465821680013461363203469428950533908552808235245466429989743038016850787380189888224414159926722559/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784,40480450661462133103402187215980726486083860668480829883110495577338048970621519705650965806315823177542570927480880633764750699807296694118324612144894222617441816285370806246372355081684306914681661613780482987116195501235195070603476189477406043936233796170219918374480045024888408816780089420034281579839280161901879959613364929652205938063807910994810169092152152274578424441506012352793628243291089511643550953344955630453747953410717864757530624269782468303167396677174059358498932808637315351189153591955662550422432790617635396560975136770439829043461261895576546316122594032994939692422461039631855468615697251826596315137/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784] does not contain Infinity

Test (Polka): -1.000000e+308 + -1.000000e+308 = -Infinity
Test Failed (double rnd): [-40480450661462133103402187215980726486083860668480829883110495577338048970621519705650965806315823177542570927480880633764750699807296694118324612144894222617441816285370806246372355081684306914681661613780482987116195501235195070603476189477406043936233796170219918374480045024888408816780089420034281579839280161901879959613364929652205938063807910994810169092152152274578424441506012352793628243291089511643550953344955630453747953410717864757530624269782468303167396677174059358498932808637315351189153591955662550422432790617635396560975136770439829043461261895576546316122594032994939692422461039631855468615697251826596315137/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784,-40480450661462115126470838592821451823158814161751640296534110418710808670231638261484039344844229857167741852732082579155921341512552329014647577055830963358087672231341133378641047084323411333096708338731428780909028559232773259433908118852541471468749109324498630538226047471129362902280775393282901974751322333314714885016411885469672543711663110667383208958222093404450203053965993019403300544638051732206717359342392055984223090990920583220905435269993413028784389461212336100655438949972983408852384612782204491640522508493753465821680013461363203469428950533908552808235245466429989743038016850787380189888224414159926722559/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784] does not contain -Infinity

Test (Polka): -1.797693e+308 + -1.797693e+308 = -Infinity
Test Failed (double rnd): [-72771428250243162760196637052415356088912527174402404482121621128145651599289463924517844009568661495109618101892402764919858241384781636359014229173033476560378327297675809908280430536566927830746469039166265813852669328521617587209585439920408557557768100794328649838480955052919571205492664143736738264388651568885067960634136906832324952635527254252446434854806554635195834071852875565011660202750510197272932644133093329281565290346795772028095413774789276032491068588316749808916466127656864144970947732005404562912698039053189123630016901203469470170663818668729161458544152493861105926195390402931520152778066420491792416769/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784,-72771428250243130443190565741411643282458570549838671641772631668861689211763063572364197775230348817475057736209741137096636401580257285878951914503279403718978637897303395312891084026775183892709509658264180542143413445856281464385806281245801821202912711954531532913898867590176119099059215177633709176511428173443730443851740090060274537228958232016177014357530766623214430205812854640320364576347399859768472163746344142959315234591157020567810730903763946755110950767006129234585590857209171762244583182325304845795318478102202294222319124288456092854714259530995229517310263392668233641151937511815960020642642877708794593279/202402253307310618352495346718917307049556649764142118356901358027430339567995346891960383701437124495187077864316811911389808737385793476867013399940738509921517424276566361364466907742093216341239767678472745068562007483424692698618103355649159556340810056512358769552333414615230502532186327508646006263307707741093494784] does not contain -Infinity

@antoinemine
Copy link
Owner

Actually, Apron does not track overflows, underflows and NaN in its floating-point semantics of the analyzed programs.
It is only expected to output an over-approximation of the set of normal (and denormal) values, taking rounding into account, not special values.
The reason is that the invariants are expressed using real arithmetic, using possible complex (well, linear) expressions. This can express a superset of normal float values but not all float values. When generating an invariant such as x in [0,+oo], it does not mean that float +oo is a possible value, but that it could not find or express a bound on the normal float values.
So, this is expected behavior.

In order to detect floating-point errors, tools (such as MOPSA) add additional checks for overflows and divisions by zero on the invariants output by polyhedra, and they maintain separate flags for possible infinities and NaN.

@svenkeidel
Copy link
Author

Makes sense, same as for integer variables.
I guess this means this issue can be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants